学习C:我的指针代码出了什么问题?

时间:2011-09-09 12:57:36

标签: c string pointers

我现在正在努力学习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 个答案:

答案 0 :(得分:2)

char string;
string = "hello!";  

第一个问题:您将string声明为单个字符,而不是数组。此外,您只能在单个语句中将数组初始化为字符串文字。

char string[] = "hello!";

第二个问题:sptr是一个指向char的指针,因此它必须指向字符串的第一个元素。其中任何一个都可以:

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

然后,在打印字符串时,只需直接传递sptr

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

编辑以获取下一个问题。

char fnamn[] = &fname;

您正在尝试将char**(指向char的指针)指定给数组。那是行不通的。如果要将fname指向的字符串复制到fnamn,则需要使用strncpy等函数。

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

话虽如此,如果你只是想打印字符串,那么直接打印fname而不先将其复制到你的数组中。

答案 1 :(得分:1)

首先,我建议改变:

char string;

为:

char *string;

很明显,您希望string变量是字符串而不是单个字符。

此外,您可能想要更改两行:

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

为:

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

但您也可以将string本身传递给printf

至于sendptr(ptr, sptr);,如果不了解更多细节,我们就无法帮助你。


要修复您的第二个功能(来自您的编辑),请更改:

char fnamn[] = &fname;

为:

char *fnamn = fname;

或直接使用fname。你没有拥有来制作指针的副本,而前者用于以下内容:

char fnamn[] = "I am a string literal";

答案 2 :(得分:1)

以下是带有一些注释的程序的更正版本:

#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;
}

答案 3 :(得分:1)

在C语言中,&运算符意味着您要使用变量的地址(即&amp; =“变量的地址”)。

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.

答案 4 :(得分:1)

我认为添加一些关于char数组和指向字符串的指针之间差异的东西可能会有所帮助。

在下面的function1中,局部变量 stringPtr 是一个指向内存的指针,其中包含字符串“hello!”。包含此字符串的内存将位于程序的只读部分。编译器决定将字符串“hello!”放在何处!并确保使用此内存地址初始化本地变量。

您可以修改指针 stringPtr 并将其更改为指向其他位置。但你无法修改它所指向的记忆。

此外,即使它是一个指针,使用数组访问符号 stringPtr [2] 也是完全有效的。

在function2中,编译器将在堆栈上为局部变量 stringArray 留出9个字节的空间,并确保使用字符串“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;
}

答案 5 :(得分:1)

首先,main的返回类型应为int,而不是void。如果编译器文档显式将其列为合法签名,则void main()只能定义良好。否则,您将调用未定义的行为。请改用int main(void)

其次,是时候对字符串,数组和指针进行快速速成课程了。

与Java不同,C没有专用的字符串数据类型;相反,字符串表示作为由{0}终止的char值的序列。它们存储作为char的数组。字符串文字“hello”存储为char(C ++中的const char)的6元素数组。此数组具有静态范围,这意味着它在程序启动时分配并保持到程序终止。试图修改字符串文字的内容会调用未定义的行为;最好表现得好像是不可写的。

当数组表达式出现在大多数上下文中时,表达式的类型将从“N元素数组T”转换为“指向T”,表达式的值是第一个元素的地址。阵列。这是string = "hello";语句不起作用的原因之一;在该上下文中,表达式"hello"的类型从“{元素char的6元素数组”转换为“指向char的指针”,这与目标类型不相容(是char,不是正确的类型)。此规则的唯一例外是当数组表达式是sizeof或一元&运算符的操作数时,或者它是一个字符串文字用于初始化声明中的另一个数组。

例如,声明

char foo[] = "hello";

foo分配为char的6元素数组,并将字符串文字的内容复制到其中,而

char *bar = "hello";

bar指定为char的指针,并将字符串文字的地址复制到其中。

如果要将一个阵列的内容复制到另一个阵列,则需要使用strcpymemcpy等库函数。对于字符串,您可以使用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的类型都将其从“{元素数组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 *

请记住,一元&运算符将产生其操作数的地址(假设操作数是左值)。这就是为什么你的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]个字节,以便它指向数组末尾的第一个字节。

答案 6 :(得分:0)

#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);
}