Изучение C: что не так в моем коде указателя? - PullRequest
0 голосов
/ 09 сентября 2011

Я сейчас пытаюсь выучить C, я прихожу с Java, и есть кое-что новое для меня.

Я хочу напечатать строку и отправить int и строку (charмассив) в другой метод.Но я продолжаю получать некоторые ошибки, которые я не знаю, как исправить.

Был бы очень признателен, если бы кто-то нашел время и объяснил мне, что не так в моем коде.Я в данный момент довольно дезориентирован с этими указателями.Когда использовать% s и% c при печати и т. Д. *

Код:

#include <stdio.h>

void main()
{
    int k = 10;
    char string;
    char *sptr;
    string = "hello!";  

    int *ptr;

    sptr = &string;

    ptr = &k; 
    printf("%s \n", &sptr);
    printf("Sending pointer.\n");

    sendptr(ptr, sptr);
}

И ошибки.

test.c: In function ‘main’:
test.c:8:9: warning: assignment makes integer from pointer without a cast
test.c:15:2: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char **’
tezt.c: In function ‘sendptr’:
tezt.c:8:8: error: incompatible types when assigning to type ‘char[6]’ from type ‘char’

Спасибо за ваше время!:)

Первые функции решены.

Вторая функция у меня получена ..

tezt.c: In function ‘sendptr’:
tezt.c:5:2: error: invalid initializer



#include <stdio.h>

void sendptr(int *test, char *fname)
{
    char fnamn[] = &fname;
    int pt;


    pt = *test;
    printf("%p \n", test);
    printf("%d \n", pt); 
    printf("%s \n", fnamn);
}

Ответы [ 7 ]

2 голосов
/ 09 сентября 2011
char string;
string = "hello!";  

Первая проблема: вы объявляете string как один символ, а не как массив. Кроме того, вы можете инициализировать массив строковым литералом только в одном выражении.

char string[] = "hello!";

Вторая проблема: sptr - это указатель на символ, поэтому он должен указывать на первый элемент вашей строки. Любой из них подойдет:

char *sptr = string;
char *sptr = &string[0];

Затем при печати строки просто передайте sptr напрямую.

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

РЕДАКТИРОВАТЬ для вашего следующего вопроса.

char fnamn[] = &fname;

Вы пытаетесь присвоить массиву char** (указатель на указатель на символ). Это просто не сработает. Если вы хотите скопировать строку, указанную fname, в fnamn, вам нужно использовать такую ​​функцию, как strncpy.

char fnamn[MAX_STRING_SIZE];
strncpy(fnamn, fname, MAX_STRING_SIZE);

Сказав это, если вы просто хотите напечатать строку, то напечатайте fname напрямую, не копируя ее сначала в ваш массив.

1 голос
/ 09 сентября 2011

Прежде всего, тип возврата для main должен быть int, а не void.void main() четко определен только в том случае, если в документации вашего компилятора явно указан его как легальная подпись.В противном случае вы вызываете неопределенное поведение.Вместо этого используйте int main(void).

Во-вторых, пришло время быстрого ускоренного курса для строк, массивов и указателей.

В отличие от Java, C не имеет выделенного типа данных строки;скорее строки представлены в виде последовательностей char значений, оканчивающихся на 0. Они хранятся в виде массивов char.Строковый литерал "hello" хранится в виде 6-элементного массива char (const char в C ++).Этот массив имеет статический экстент, что означает, что он выделяется при запуске программы и удерживается до ее завершения.Попытка изменить содержимое строкового литерала вызывает неопределенное поведение;Лучше всего вести себя так, как будто они не написаны.

Когда выражение массива появляется в большинстве контекстов, тип выражения преобразуется из «N-элементного массива T» в «указатель на T», а значением выражения является адрес первогоэлемент массива.Это одна из причин, по которой оператор string = "hello"; не работает;в этом контексте тип выражения "hello" преобразуется из «6-элементного массива char» в «указатель на char», что несовместимо с целевым типом (который, будучи char, не являетсяв любом случае это неправильный тип).Единственными исключениями из этого правила являются случаи, когда выражение массива является операндом операторов sizeof или унарных & или если это строковый литерал, используемый для инициализации другого массива в объявлении.

Например, объявление

char foo[] = "hello";

выделяет foo как массив из 6 элементов char и копирует содержимое строкового литерала в неготогда как

char *bar = "hello";

выделяет bar в качестве указателя на char и копирует адрес строкового литерала в него.

Если вы хотите скопировать содержимое одного массива в другой, вам нужно использовать библиотечную функцию, такую ​​как strcpy или memcpy.Для строк вы должны использовать strcpy следующим образом:

char string[MAX_LENGTH];
strcpy(string, "hello");

Вам нужно убедиться, что цель достаточно велика, чтобы хранить содержимое строки источника вместе с завершающим 0.В противном случае вы получите переполнение буфера.Массивы в C не знают, насколько они велики, и запуск после конца массива не вызовет исключения, как в Java.

Если вы хотите защититься от возможности переполнения буфера,вы бы использовали strncpy, который принимает значение в качестве дополнительного параметра, так что копируется не более N символов:

strncpy(string, "hello", MAX_LEN - 1);

Проблема в том, что strncpy не будет добавлять терминатор 0к цели, если источник длиннее пункта назначения;вам придется сделать это самостоятельно.

Если вы хотите напечатать содержимое строки, вы должны использовать спецификатор преобразования %s и передать выражение, которое оценивает адрес первого элемента строки, например:

char string[10] = "hello";
char *p = string;

printf("%s\n", "hello");  // "hello" is an array expression that decays to a pointer
printf("%s\n", string);   // string is an array expression that decays to a pointer
printf("%s\n", p);        // p is a pointer to the beginning of the string

Опять же, оба типа "hello" и string преобразованы из "N-элементного массива char" в "указатель на char";все, что видит printf - это значение указателя.

Вот удобная таблица, показывающая типы различных выражений, включающих массивы:

Declaration: T a[M];

Expression         Type            Decays to
----------         ----            ---------
         a         T [M]           T *
        &a         T (*)[M]
        *a         T
      a[i]         T
     &a[i]         T *

Declaration: T a[M][N];

Expression         Type            Decays to
----------         ----            ---------
         a         T [M][N]        T (*)[N]
        &a         T (*)[M][N]     
        *a         T [N]           T *
      a[i]         T [N]           T *
     &a[i]         T (*)[N]
     *a[i]         T 
   a[i][j]         T
  &a[i][j]         T *

Помните, что унарный оператор & выдаст адрес своего операнда (при условии, что операнд является lvalue).Вот почему ваша char fnamn[] = &fname; декларация вызвала ошибку «неверный инициализатор»;вы пытаетесь инициализировать содержимое массива char значением указателя.

Унарный оператор * выдаст значение любого операнда , указывающего на .Если операнд не указывает ни на что значащее (либо NULL, либо не соответствует действительному адресу), поведение не определено.Если вам повезет, вы получите прямую ошибку.Если вам не повезет, вы получите странное поведение во время выполнения.

Обратите внимание, что выражения a и &a дают одинаковое значение (адрес первого элемента в массиве), но их типы различны. Первая выдает простой указатель на T, а вторая - указатель на массив T. Это важно, когда вы выполняете арифметику указателей. Например, предположим следующий код:

int a[5] = {0,1,2,3,4};
int *p = a;
int (*pa)[5] = &a;

printf("p = %p, pa = %p\n", (void *) p, (void *) pa);
p++;
pa++;
printf("p = %p, pa = %p\n", (void *) p, (void *) pa);

Для первого printf два значения указателя идентичны. Затем мы продвигаем оба указателя. p будет продвигаться на sizeof int байт (то есть будет указывать на второй элемент массива). pa, OTOH, будет расширен на sizeof int [5] байтов, так что он будет указывать на первый байт после конца массива.

1 голос
/ 09 сентября 2011

Я подумал, что было бы полезно добавить кое-что о разнице между массивом символов и указателем на строку.

В функции ниже 1 локальная переменная stringPtr - это указатель на память, которая содержит строку «привет!». Память, содержащая эту строку, будет расположена в разделе «только для чтения» программы. Компилятор решает, где разместить строку "привет!" и гарантирует, что ваша локальная переменная инициализируется с этим адресом памяти.

Вы можете изменить указатель stringPtr и изменить его так, чтобы он указывал на другое место. Но вы не можете изменить память, на которую он указывает.

Также вполне допустимо использовать обозначение доступа к массиву stringPtr [2] , даже если это указатель.

В функции 2 компилятор выделит 9 байтов пространства в стеке для локальной переменной stringArray и обеспечит инициализацию этого массива строкой "Goodbye!". Поскольку эта память находится в стеке, вы можете изменять содержимое массива.

#include <stdio.h>

void function1(void)
{
  char *stringPtr = "hello!";

  printf("The first char is %c\n", stringPtr[0]);

  printf("The next char is %c\n", *(stringPtr+1));

      // This would cause a segmentation fault, stringPtr points to read-only memory
      // stringPtr[0] = 'H';
}

void function2(void)
{
  char stringArray[] = "Goodbye!"; 
  printf("The first char is %c\n", stringArray[0]);
}

int main(void)
{
  function1();
  function2();
  return 0;
}
1 голос
/ 09 сентября 2011

В языке C оператор & означает, что вы хотите использовать адрес переменной (т. Е. & = "Адрес переменной").

int an_integer=2; // an_integer is a memory part where you want to store 2 ;)

printf("%d", &an_integer); // here you will print the address of the memory part where an_integer is stored (not 2, more  something like 2510849).

Оператор * вобъявление переменной означает, что вы хотите иметь указатель на часть памяти, при использовании его в коде это означает «значение, содержащееся по адресу»

int an_integer=2;
int *ptr_integer; // you declare a pointer to an integer

ptr_integer = &an_integer; // here you set the pointer ptr_integer to the address of an_integer

printf("%d", *ptr_integer); // here you print the value contained at the memory address stored in the ptr_integer

Оператор []означает, что вы хотите хранить массив чего-то.В C массив можно рассматривать как указатель на область памяти.

int an_integer[2]; // you declare an array of 2 integers
int *ptr_integer; // you declare a pointer to an integer

ptr_integer = (int *)an_integer; // here you set the value of the pointer to the address of the array, you have to cast it into an (int *) to avoid compilation warnings.
1 голос
/ 09 сентября 2011

Вот исправленная версия программы с некоторой аннотацией:

#include <stdio.h>

int main(void) // int and (void) for standard mains.
{
  int k = 10;
  char *string; // a C string is a char array, you need a pointer to point to it
  char *sptr;
  int *ptr;

  string = "hello!";

  sptr = string;
  ptr = &k;

  printf("%s \n", sptr); // no &. The %s format expects a char*.
  printf("Sending pointer.\n");

  // sendptr(ptr, sptr);  // don't know what this function is, ignoring

  return 0;
}
1 голос
/ 09 сентября 2011

Для начала я бы предложил изменить:

char string;

на:

char *string;

Совершенно очевидно, что вы хотите, чтобы переменная string была строкой , а не один символ.

Кроме того, вы, вероятно, хотите изменить две строки:

sptr = &string;
printf("%s \n", &sptr);

на:

sptr = string;
printf("%s \n", sptr);

, но вы могли быс тем же успехом просто передайте string самому printf.

Что касается sendptr(ptr, sptr);, мы не можем ничего поделать, не зная больше деталей об этом.


Toисправьте вторую функцию (из вашего редактирования), измените:

char fnamn[] = &fname;

на:

char *fnamn = fname;

или просто используйте fname напрямую.У вас нет , чтобы сделать копию указателя, а первый предназначен для таких вещей, как:

char fnamn[] = "I am a string literal";
0 голосов
/ 09 сентября 2011
#include <stdio.h>

void main()
{
  int k = 10;
  char string;
  char *sptr;
  sptr = "hello!";  

  int *ptr;

  ptr = &k; 
  printf("%s \n", sptr);
  printf("Sending pointer.\n");

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