Почему следующее присваивание фрагментов кода дает запутанный вывод? - PullRequest
0 голосов
/ 04 января 2019

Я изучаю C. Я наткнулся на строковые массивы.Я немного запутался по поводу следующих кодов.Я ожидал один вид выхода;однако, из-за нарушения прав доступа чтения получается совершенно другой вид вывода или программный сбой.

Я запускал этот код в Visual Studio 2017 с _CRT_SECURE_NO_WARNINGS

// 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 голосов
/ 04 января 2019

Неясно, выполняется ли код 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;
}

См. Живой код здесь

OP должен заменить одинарные кавычки вокруг doe с двойными кавычками, которые обозначают строку с нулевым символом в конце.Одиночные кавычки предназначены для одного символа, то есть «a» относится к значению байта, в то время как «a» обозначает строку, содержащую два символа: «a» и «\ 0».

Также OP долженвключить две другие библиотеки для облегчения выполнения.В частности, string.h необходим для правильного выполнения встроенных строковых функций.

Случай 2 и Случай 3 работают, потому что строки заключаются в двойные кавычки вместо одинарных кавычек.Обратите внимание, что в каждом случае спецификатор формата "% s" для printf () указывает, что строка должна быть отображена.

В последнем случае fgets () относительно stdin при успешном завершении возвращает пользовательский ввод какстрока.Но этот вход будет переопределен в цикле 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

См. живой код.

Кстати, вам не нужен массив для отображения имен идействительно, можно отображать имена вне цикла, если в цикле код объединяет пользовательский ввод.

0 голосов
/ 04 января 2019

Для случая 1 'doe' не является строкой.

Случай 2 работает, потому что вы инициализируете свои указатели строковыми литералами.

Случай 3 также работает, потому что вы присваиваете тот же инициализированныйуказатель в случае 2 указатель в случае 1.Ваши указатели массива имен в основном настроены так, чтобы указывать на те, на которые указывают name2.

В случае 4 вы объявили inputName, который указывает на набор из 10 символов.Затем каждый раз, когда вы получаете новый ввод, вы записываете его в один и тот же раздел памяти.Делая это: name[i++] = inputName; вы не копируете новый массив символов для имени [i], как вы могли бы подумать.Вместо этого вы сообщаете name [i] char указатель, указывающий на inputName.Таким образом, нормально, что name печатает последний ввод дважды, потому что именно на это указывает inputName, а также оба указателя на имя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...