【问题标题】:Pointer assignment to different types不同类型的指针赋值
【发布时间】:2019-09-03 04:44:40
【问题描述】:

我们可以在 C 中分配一个字符串,如下所示:

char *string;
string = "Hello";
printf("%s\n", string); // string
printf("%p\n", string); // memory-address

并且可以按如下方式进行编号:

int num = 4404;
int *nump = #
printf("%d\n", *nump);
printf("%p\n", nump);

那么我的问题是,为什么我们不能像处理字符串一样在 C 中分配一个指向数字的指针?例如,做:

int *num;
num = 4404;
// and the rest...

是什么让字符串与其他原始类型有根本的不同?我对 C 很陌生,所以任何关于两者之间差异的解释都会非常有帮助。

【问题讨论】:

标签: c


【解决方案1】:

C 中没有“字符串”这样的类型。字符串不是原始类型。字符串只是一个字符数组,以 NUL 字节 ('\0') 结尾。

当你这样做时:

char *string;
string = "Hello";

真正发生的是编译器很聪明,它创建了一个只读常量char 数组,然后将它分配给您的变量string。可以这样做是因为在 C 中,数组的名称与指向其第一个元素的指针相同。

// This is placed in a different section:
const char hidden_arr[] = {'H', 'e', 'l', 'l', 'o', '\0'};

char *string;
string = hidden_arr;
// Same as:
string = &(hidden_arr[0]);

这里,hidden_arrstring 都是char *,因为正如我们刚才所说,数组的名称等于指向其第一个元素的指针。当然,所有这些都是透明地完成的,您实际上不会看到另一个名为hidden_arr 的变量,这只是一个示例。实际上,该字符串将存储在可执行文件中的某个位置而没有名称,并且该位置的地址将被复制到您的 string 指针。

当你尝试对整数做同样的事情时,这是错误的,因为 int *int 是不同的类型,你不能写这个(好吧,你可以,但它没有意义,并且不会按照你的预期去做到):

int *ptr;
ptr = 123;

但是,您可以很好地使用 整数数组

int arr[] = {1, 2, 3};
int *ptr;
ptr = arr;
// Same as:
ptr = &(arr[0]);

【讨论】:

  • 可以解释,但细节有问题。 C string 不是 array,尽管 string literal 是。 string literal "Hello" 不是只读的,因为它不应该被写入 - 结果是 UB - 它可能有效,也可能无效,可能会杀死代码。 hidden_arr(字符串文字)不是char *,因为数组不是指针,指针也不是数组。使用 int *ptr; ptr = 123;,代码可以在非常有限的值条件下做到这一点 - int *int 因为不同的类型不是限制。
  • @chux 没有说 char 数组是指针。我特别说过,char 数组的名称和指向其第一个元素的指针是一回事。执行 int *x = 123 会警告您类型不匹配,因此不应在没有强制转换的情况下完成。标准可能不要求字符串文字“hello”为 RO,但写入它是 UB,因此您不妨考虑它为 RO,实际上通常是 RO,因为它被编译器放入 RO 部分。
  • "我们刚刚说过数组的名称等于指向其第一个元素的指针。"混淆了更多的数组/指针差异 --> 反例:比较 sizeof hidden_arrsizeof (char *) - 它们不同 - 数组的名称不总是等于指向第一个元素的指针。警告是一个很好的指示,表明某些事情可能有问题,但也可能没有,否则编译器会出错,而不是简单地发出警告。 Cast 确实消除了警告。一个兼容的编译器/平台不需要任何“只读”内存——在嵌入式设备和简单处理器中很常见,以至于缺少它。
  • “比较 sizeof hidden_​​arr 和 sizeof (char *) - 它们不同” 我很清楚。这:“数组的名称等于指向其第一个元素的指针”是正确的。 arr&(arr[0]) 之间没有区别。同样,我没有说数组是指针,我知道它们是完全不同的东西。 --- 根据字符串,当然,但将其视为 RO 是您唯一可以合法做的事情,所以这就是我写的。这对初学者来说是一个简单的答案,无需深入了解 C 标准。
  • “否则编译器会出错,而不是简单地警告” 嗯...许多警告导致运行时错误或 UB .在不进行适当转换的情况下将一种类型分配给另一种类型是一个非常有用的警告,不应忽略。这只是警告 可以 被安全忽略的情况,但绝对不应该这样做,这对于初学者来说只是一个简单的答案。
【解决方案2】:

为什么我们不能像处理字符串一样在 C 中分配一个指向数字的指针?

int *num;
num = 4404;

代码可以做到这一点如果 4404 是int 的有效地址。

整数可以转换为任何指针类型。除先前规定外, 结果是实现定义的,可能没有正确对齐,可能不指向 引用类型的实体,可能是陷阱表示。
C11dr §6.3.2.3 5

如果地址没有正确对齐 --> 未定义的行为 (UB)。
如果地址是一个陷阱 --> 未定义的行为 (UB)。

除非指针指向有效的int,否则尝试取消引用该指针是一个问题。

printf("%d\n", *num);

在下面,"Hello" 是一个字符串文字。它存在于某个地方。该赋值采用 字符串文字 的地址并将其分配给string

char *string;
string = "Hello";

关键是分配的地址已知对char *有效。

num = 4404; 中不知道是有效的(它可能不是)。


是什么让字符串与其他原始类型有根本的不同?

在 C 中,string 是 C 库规范,而不是 C 语言规范。定义方便,便于解释其中的各种功能。

string 是一个连续的字符序列,由第一个空字符 §7.1.1 1

终止并包括第一个空字符

原始类型是 C 语言的一部分。

这些语言也有字符串字面量,例如char *string; string = "Hello"; 中的"Hello"。它们与 strings 有一些相似之处,但又有所不同。


我建议搜索“ISO/IEC9899:2017”以找到当前 C 规范的草稿副本。它将回答您上周的 10 个问题中的许多问题。

【讨论】:

    【解决方案3】:

    是什么让字符串与其他原始类型有根本的不同?

    字符串看起来类似于 C 中的原始类型,因为编译器理解 "foo" 并生成一个以 null 结尾的字符数组:['f', 'o', 'o', '\0']。但是 C 字符串仍然只是:一个字符数组。

    那么我的问题是,为什么我们不能像处理字符串一样在 C 中分配一个指向数字的指针?

    你当然可以给数字赋值,只是数字不是指针,而数组的是数组的地址。如果你有一个int 的数组,那么它就像一个字符串一样工作。比较您的代码:

    char *string;
    string = "Hello";
    printf("%s\n", string); // string
    printf("%p\n", string); // memory-address
    

    到整数数组的类似代码:

    int numbers[] = {1, 2, 3, 4, 5, 0};
    int *nump = numbers;
    printf("%d\n", nump[0]); // string
    printf("%p\n", nump);    // memory-address
    

    唯一真正的区别是编译器对字符数组有一些额外的语法,因为它们很常见,而printf() 同样有一个格式说明符,只是出于同样的原因。

    【讨论】:

    • 感谢您。这部分对理解它特别有帮助:The only real difference is that the compiler has some extra syntax for arrays of characters because they're so common, and printf() similarly has a format specifier just for character arrays for the same reason.
    【解决方案4】:

    字符串文字(例如“hello world”)的类型是char[]。其中分配char *string = "Hello" 意味着string 现在指向数组的开头(例如数组中第一个内存地址的地址:&char[0])。

    您不能将整数分配给指针,因为它们的类型不同,一个是int,另一个是指针int *。您可以将其转换为正确的类型:

    int *num;
    num = (int *) 4404;
    

    但这会被认为是非常危险的(除非你真的知道自己在做什么)。 IE。你知道什么是内存地址4404吗?

    【讨论】:

      猜你喜欢
      • 2019-08-09
      • 1970-01-01
      • 2013-12-12
      • 2021-04-07
      • 2021-06-07
      • 2019-06-05
      • 1970-01-01
      • 2012-03-13
      • 2017-03-26
      相关资源
      最近更新 更多