C指针概念分段错误

ct2axkht  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(103)

请看一下代码片段:我不明白为什么代码会给出分段错误和垃圾字符。

#include <stdio.h>

int main()
{
    char *str[1];
    str[0] = "apple";

    char **ptr = str;
    char *p = str;

    printf("%d\n", ptr);  //---> 1956347328
    printf("%d\n", p);      //---> 1956347328

    printf("%s \n", *ptr, ); // ---> prints apple
    printf("%s\n", *p);    // ---> error segmentation fault
    printf("%s\n", p);    // ---> Garbage characters    
}

字符串

bvn4nwqk

bvn4nwqk1#

char *p=str;是一个不兼容的指针类型初始化,因为str是一个char *[1]。也许你指的是char *p = *str
printf("%d\n",ptr)不是打印指针的方式。在我的系统中,int(对于%d)是4字节,指针是8字节。相反,你应该打印printf("%p\n", (void *) ptr)
printf("%s \n",*ptr,);是由于缺少参数而导致的语法错误。
我想这就是你想要的:

#include <stdio.h>

int main(void) {
    char *str[1];
    str[0] = "apple";

    char **ptr = str;
    char *p=*str;

    printf("%p\n", (void *) ptr);
    printf("%p\n", (void *) p);

    printf("%s \n",*ptr);
    printf("%s\n", p); 
    printf("%c\n", *p);

}

字符串
输出为:

0x7fffcb2bb178
0x563522724004
apple 
apple
a

sg24os4d

sg24os4d2#

下面是发布的代码行为和问题的详细说明:

  • char *str[1];定义了一个指向char的指针数组,其中包含一个元素。
  • str[0] = "apple";可能会触发警告,因为您将指向字符串文字的指针存储到数组的元素中,该元素包含指向可修改数据的指针。只要您不使用此指针来实际尝试和修改字符串,您就是安全的。
  • char **ptr = str;定义了另一个对象,一个指向char的指针,初始化后指向数组str。这没有问题,但是你只能访问ptr[0]上的元素。
  • char *p = str;定义了另一个对象,一个指向char的指针被初始化为指向数组str。这是一个类型不匹配,因为str的类型既不是 *array of char * 也不是 *pointer to char *,因此编译器应该发出警告。然而,只要你只使用这个指针来访问组成这个数组的字节,这个数组包含一个指针(指向"apple"字符串),在64位机器上它由8个字节组成。
  • printf("%d\n", ptr)具有未定义的行为,因为printf期望%dint类型的参数,并且您传递了char **值。在您的体系结构中,整数和指针似乎在同一个寄存器或堆栈区域中传递,因此printf输出的整数是一部分(低32位)的指针,1956347328。你应该改为使用printf("%p\n", (void *)ptr);
  • printf("%d\n", p)如上所述,输出是相同的,因为两个指针指向相同的地址,但由于它们具有不同的类型,解引用它们会产生不同的东西。
  • printf("%s \n", *ptr, )是一个语法错误,但没有额外的,printf接收*ptr,这是数组str的第一个元素,即:char *指向字符串"apple",这是它对%s的期望。printf输出字符串apple,一个空格和一个换行符。
  • printf("%s\n", *p)printf接收*p,它是str数组开头的char。这个字节是指向字符串的指针的第一个字节,它有256个可能的值之一,在CHAR_MINCHAR_MAX范围内(0..255或-128..127取决于char类型的符号). printf需要一个指向char的指针作为%s转换的参数。传递char值具有未定义的行为,并且在您的情况下,printf使用的值是一个无效的指针:解引用它会导致分段错误。
  • printf("%s\n", p):指针p被传递到printf进行%s转换,这很好,但是printf读取并输出它所指向的字节,直到它读取空字节。这些字节不是字符串apple的字节,它们是组成存储在str[0]中的指针的字节,如果printf在读取空字节时没有找到空字节,那么当阅读超过数组末尾时,它将调用未定义的行为,并可能导致分段错误。

考虑这个修改版本:

#include <stdio.h>

void dump(const char *s, void *addr, size_t len) {
    printf("%s is at address %p, contains:", s, addr);
    unsigned char *p = addr;
    for (size_t i = 0; i < len; i++)
        printf(" %02x", p[i]);
    printf("\n");
}

int main(void) {
    char *str[1] = { (char *)"apple" };
    char **ptr = str;
    char *p = (char *)str;

    dump("str ", &str, sizeof(str));
    dump("ptr ", &ptr, sizeof(ptr));
    dump("p   ", &p, sizeof(p));

    dump("*ptr", *ptr, sizeof("apple"));

    printf("*ptr as string: %s\n", *ptr);
    printf("*p   as byte  : %02x\n", *p);

    return 0;
}

字符串
我在我的MacBook上得到这个输出:

str  is at address 0x30cb5f278, contains: 6b ef 3d 04 01 00 00 00
ptr  is at address 0x30cb5f270, contains: 78 f2 b5 0c 03 00 00 00
p    is at address 0x30cb5f268, contains: 78 f2 b5 0c 03 00 00 00
*ptr is at address 0x1043def6b, contains: 61 70 70 6c 65 00
*ptr as string: apple
*p   as byte  : 6b


如果我再次运行相同的可执行文件,我会得到不同的输出,因为地址空间随机化,一种用于使黑客更难利用软件漏洞的技术。
输出显示:

  • 3个对象pptrstr具有8字节的大小。
  • 它们位于内存中相邻的位置(在堆栈中,在main参数下面)。
  • pptr指向str占用的区域:地址0x3085b8280以小端顺序存储,即:从最低有效字节到最高有效字节。这看起来像 * 逆序 *,但存储指针字节的顺序(或整数)只是一个约定的问题,就像短语中词性的顺序从一种语言到另一种语言不同一样。
  • 包含字节61 70 70 6c 65 00的串"apple"位于地址0x1023dff58(恒定数据段)。
  • *p的值为6b,它是一个k,但是,在我的笔记本电脑上,这个值会随着运行的不同而变化,正如上面所解释的。
zazmityj

zazmityj3#

在表达式中使用的数组(很少有例外)被隐式转换为指向其第一个元素的指针。
所以数组str声明为

char * str[1];

字符串
在这些声明中用作初始化器

char **ptr = str;
char *p=str;


被隐式转换为char **类型的指针。
对于第二个声明,编译器应该发出一条消息,指出使用了不兼容的指针类型:char *char **
printf的这些调用

printf("%d\n",ptr);  //---> 1956347328
printf("%d\n",p);      //---> 1956347328


是无效的,因为使用了不正确的转换说明符d,该转换说明符被设计为输出int类型的对象和指针类型的对象。相反,你应该写

printf("%p\n", ( void * )ptr);    //---> 1956347328
printf("%p\n", ( void * )p);      //---> 1956347328


然而,从输出中可以看出,这两个指针都包含数组str的第一个元素的地址。因此,解引用char **类型的指针,您将获得char *类型的数组str的第一个元素,因此可以输出该元素所指向的字符串:

printf("%s \n",*ptr,);


在这个printf的调用中

printf("%s\n",*p);


表达式*p的类型为char。结果,上面显示的地址中只有一个字节被读取,然后被提升为类型int,并被用作有效地址。结果,发生分段错误。
在这个printf的调用中

printf("%s\n",p);


而不是像在这个调用中那样使用数组str的第一个元素(指针)的值

printf("%s \n",*ptr,);


使用第一元素的地址,并且第一元素的存储指针值作为字符串输出。
相反,你可以写例如

printf("%s\n", *( char ** )p);


发出类似于

printf("%s \n",*ptr,);


正确输出数组str的第一个元素所指向的字符串。

ao218c7q

ao218c7q4#

你将一个指针赋值给一个字符串数组(str[0] =“apple”;)。然而str被声明为一个指向字符的指针数组。如果你想把它用作一个字符数组,你应该使用char str[] =“apple”;。
char p = str;正在尝试分配一个char*。我想下面的代码会对你有所帮助。

#include <stdio.h>

int main() {
    char str[] = "apple";  // Correctly initialize str as an array of characters

    char *ptr = str;       // Use char * instead of char ** to assign the pointer to str

    printf("%p\n", (void *)ptr);  // Use %p to print pointers 
    printf("%p\n", (void *)str); //Print the address of the str(same as above line)

    printf("%s\n", ptr);   // Prints "apple"
    printf("%s\n",str);   //Prints "apple"
    printf("%c\n", *ptr);  // Prints 'a', accessing individual character
    
    return 0;
}

字符串
产出:

0x7ffc41ae4a02
0x7ffc41ae4a02
apple
apple
a

相关问题