为什么遵循以下代码片段分配会产生混乱的输出?

时间:2019-01-03 23:26:53

标签: c arrays string

我正在学习C。我遇到了字符串数组。我对以下代码感到困惑。我正在期待一种输出。但是,由于读取访问冲突,导致获得完全不同的输出或程序崩溃。

我已使用_CRT_SECURE_NO_WARNINGS在Visual Studio 2017上运行此代码

// case 1

char* name[2];
//name[0] = (char*)malloc(sizeof(char*) * 10); 
//name[1] = (char*)malloc(sizeof(char*) * 10);
name[0] = "john";
name[1] = 'doe';
printf("%s\n", name[0]); // prints john
//printf("%s\n", name[1]); // gives read access violation exception, why??? even with dynamically allocated memory

// case 2

char* name2[2] = { "emma", "olsson" };
printf("%s\n", name2[0]); // prints emma
printf("%s\n", name2[1]); // prints olsson, why no error???

// case 3

for (int i = 0; i < 2; i++)
{
    name[i] = name2[i];
}
printf("%s\n", name[0]); // prints emma
printf("%s\n", name[1]); // prints olsson, why no error??? 

// case 4

char inputName[10];
int i = 0; 
while (i < 2)
{
fgets(inputName, sizeof(inputName), stdin); // first input: Max   second input: Payne
char* pos = strchr(inputName, '\n');
if (pos != NULL)
    *pos = '\0';
name[i++] = inputName;
}
printf("%s\n", name[0]); // prints Payne, why not Max???
printf("%s\n", name[1]); // prints Payne

2 个答案:

答案 0 :(得分:2)

对于情况1,“ doe”不是字符串。

第2种情况之所以有效,是因为您正在用字符串文字初始化指针。

案例3也是可行的,因为您将案例2中相同的初始化指针分配给案例1指针。您的名称数组指针基本上设置为指向name2指向的位置。

在情况4中,您声明了inputName,它指向一组10个字符。然后,每当您收到新输入时,便会将其写入相同的内存部分。然后执行以下操作:name[i++] = inputName; 您没有像您想的那样将新的char数组复制到name [i]。相反,您要告诉name [i] char指针指向inputName。因此,正常情况下,名称将最后一次输入打印两次,因为这是inputName指向的内容,以及两个名称char指针。

答案 1 :(得分:0)

尚不清楚OP的代码是在main()还是用户定义的函数中运行以及返回哪种类型的值。就是说,在删除了多余的变量重新声明之后,这就是我实现工作代码的方式:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {

char * name[2];
char * name2[2]={ "emma", "olsson" };
char inputName[10];
char names[BUFSIZ];
int i = 0; 

// case 1
name[0] = "john";
name[1] = "doe";
printf("%s %s\n", name[0],name[1]); //john doe

// case 2
printf("%s %s\n", name2[0],name2[1]);//emma olsson

// case 3
for (i = 0; i < 2; i++){
    name[i] = name2[i];
}
printf("%s %s\n", name[0],name[1]);//emma olsson

// case 4
i=0;
while (fgets(inputName, sizeof(inputName), stdin) != NULL && (i < 2) ){
    strcat(names,inputName);
    i++;
}
printf("\n%s\n",names);
return 0;
}

查看实时代码here

OP应该用双引号替换doe周围的单引号,双引号表示以空值结尾的字符串。单引号用于单个字符,即“ a”表示字节值,而“ a”表示包含两个字符,即“ a”和“ \ 0”的字符串。

此外,OP还应包括其他两个库以方便执行。特别是,string.h是内置字符串函数正确执行所必需的。

案例2和案例3之所以起作用,是因为字符串用双引号而不是单引号引起来。请注意,在每种情况下,printf()的“%s”格式说明符均指示需要显示字符串。

在最后一种情况下,关于stdin的fgets()成功后将用户输入作为字符串返回。但是该输入将在while循环中被覆盖,除非在每次迭代中将旧输入与新输入连接在一起。否则,当inputName元素的值由于其地址保持不变而更改时,仅显示最新的输入字符串。这是一些说明这一点的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
char * name[2];
char inputName[10];
int i = 0; 

while (fgets(inputName, sizeof(inputName), stdin) != NULL && (i < 2) ){
    printf("inputName: %p points to: %s",inputName,inputName);
    name[i++] = inputName;

}
printf("\n  name[0]: %p points to: %s\n  name[1]: %p points to: %s",name[0],name[0],name[1],name[1]);
return 0;
}
 Output:

 inputName: 0x7fff8a511a50 points to: Maxine
 inputName: 0x7fff8a511a50 points to: Pauline
   name[0]: 0x7fff8a511a50 points to: Pauline  
   name[1]: 0x7fff8a511a50 points to: Pauline

请参见live code.

顺便说一句,您不需要一个数组来显示名称,实际上只要在代码中将用户输入连接起来的循环中,就可以在循环外显示名称。