C语言速成手册(四):指针、动态内存分配、标准输入

指针的定义
    定义一个指针的方法如下:
类型名 *指针名;
    例如,下面的语句定义了一个指针:
int *pointer;
    这样,pointer就是一个指针,它指向的是一个int类型的数据。

    一个指针可以是一个合法的内存地址,也可以为0(通常写成NULL)。
    你可以用printf语句输出一个指针,对应的标识为"%p"。下面的代码可以输出上面定义的指针指向的地址。
printf("%p",p);

取地址与引用
    假如a是一个变量,p是一个指针,那么&a返回该变量的地址,*p返回该指针所指的内容(称做“引用”)。
    阅读下面的代码片段:
int *p;
int a = 520;
p = &a;
printf( "%p -> %dn", p, *p );
*p = 1314;
printf( "%p -> %dn", p, *p );
printf( "a = %d", a );

    程序输出如下。当执行了p=&a后,存取*p就相当于是存取变量a了。
0022FF78 -> 520
0022FF78 -> 1314
a = 1314

动态内存分配
    首先介绍sizeof函数(准确地说是一个运算符),它的参数为一个变量名或类型名,返回的是它所占内存空间的大小。下面的代码输出1 8 800 4 1 。
long long a;
double b[100];
_Bool *c;
printf( "%d " , sizeof(char) );
printf( "%d " , sizeof(a) );
printf( "%d " , sizeof(b) );
printf( "%d " , sizeof(c) );
printf( "%d " , sizeof(*c) );

    下面介绍四种动态内存分配函数,使用它们前需要在程序最前面包含头文件stdlib.h。四种函数的格式分别为:
void *malloc ( size );
void *calloc ( n, size );
void free ( pointer );
void *realloc( pointer, size );

    函数malloc将在内存里寻找一个大小为size的连续空间,把分配到的内存地址作为一个指向void类型的指针(默认的无类型指针)返回。如果空间分配失败,函数返回NULL。
    函数calloc将在内存里寻找一个大小为n * size的连续空间,并且把这段内存的数据全部清0,返回数据和malloc一样。如果空间分配失败,函数返回NULL。
    函数free用于释放内存空间,释放后的空间被回收,可以用于以后的malloc或calloc操作。
    函数realloc在保证已有数据不变的情况下改变已有指针的空间大小,返回重新分得的空间的内存地址(有可能和原来不同)。如果空间重新分配失败,函数返回NULL。
    Pascal中的new语句可以用前两个函数代替,free语句则相当于Pascal中的dispose。

    注意,malloc和calloc函数所返回的指针还没确定类型,理论上需要用类型转换。下面的程序合法地为p指针分配空间:
int *p;
p = (int *) malloc( sizeof(int) );
*p = 520;

    事实上,由于赋值时C语言自动转换类型,因此那个类型转换是没有必要的(去掉(int *)没有影响)。

指针与结构
    一个指针可以指向一个结构,一个结构也可以包含一个指针。结构里包含一个指向结构的指针就构成了链表:
struct node{
   int value;
   struct node *next;
}

    这样,定义struct node *a,则(*a).next就是另一个指向node结构的指针。在C语言中,(*x).y的句型很常用,因此有一个专门的记号x->y来代替(*x).y这样繁杂的写法。
    你可以从下面的程序中看到链表的使用。
#include <stdio.h>
#include <stdlib.h>
    
struct node
{
   int value;
   struct node *next;
};

int main()
{
    struct node *head = NULL;
    int i;

    for(i=1;i<=10;i=i+1)
    {        
        struct node *newNode;
        newNode = malloc( sizeof(struct node) );
        newNode->value = i;
        newNode->next = head;
        head = newNode;
    }
    
    struct node *p = head;
    while (p)
    {
        printf( "%dn", p->value );
        p = p->next;
    }
    return 0;
}

指针与函数
    前面说过,C语言中的函数参数和变量只能够供该函数使用。
    下面四个程序代码的输出分别是什么?

    代码一:
#include <stdio.h>
void swap( int a, int b )
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   int a = 520, b = 1314;
   swap( a , b );
   printf( "%d %d", a, b );
   return 0;
}

    代码二:
#include <stdio.h>
int a = 520, b = 1314;
void swap( int a, int b )
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   swap( a , b );
   printf( "%d %d", a, b );
   return 0;
}

    代码三:
#include <stdio.h>
int a = 520, b = 1314;
void swap()
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   swap();
   printf( "%d %d", a, b );
   return 0;
}

    代码四:
#include <stdio.h>
void swap( int *a, int *b )
{
   int c = *a;
   *a = *b;
   *b = c;
}
int main()
{
   int a = 520, b = 1314;
   swap( &a, &b);
   printf( "%d %d", a, b );
   return 0;
}

    答案:前两个程序输出520 1314,后两个程序输出1314 520。
    前两个程序中,待交换的两个数(即使是全局变量)作为参数传给了swap函数,该函数里的操作对函数外无影响。
    第三个程序中,swap函数对全局变量直接进行操作,其影响是全局的。
    最后一个程序巧妙地应用了指针来实现两数交换。函数的参数是指针类型,这个函数不能改变指针本身,但可以改变指针所指的内容。这是写此类函数通常所用的方法。
    为了强调函数void swap( int *a, int *b )中的指针本身不发生改变,很多地方喜欢写成void swap(const int *a, const int *b ) 。

指针与数组
    数组由内存的连续空间构成,因此可以用指

14 条评论

  • dd

    个人认为,在涉及到动态内存分配的时候,应当用new/delete而非C-style。说的严肃点是类型安全,说得轻松点就是写起来简单。

  • dd

    我觉得你这个系列不应该局限于纯C。在使用C++的某些“语法糖”的确会更方便的时候,为什么不混入一点C++风格呢?你的目标读者是OIers,那就应该介绍对于OIers最妥当的风格吧。

    回复:受我选用的参考书的限制,C++的东西我还不会;我以后再介绍C++好么

  • 123

    如果有人上不了oibh,
    C:WinntSystem32DriversEtchosts加上
    60.195.249.216 http://www.oibh.org

    回复:谢

  • 逆铭

    个人认为,在涉及到动态内存分配的时候,应当用new/delete而非C-style。说的严肃点是类型安全,说得轻松点就是写起来简单。

    dd牛说得对。而且new和delete比malloc和free都快不少:一个是函数,一个是运算符

  • 两个月亮

    sizeof 是运算符,只是很像函数,呵呵
               飘过~~~~

    回复:谢,已改正

  • digiter

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
       int i, *a;
       a = calloc(sizeof(int),5);
       for ( i=0; i<5; i=i+1 ) *(a+i)=i;
       for ( i=0; i<5; i=i+1 ) printf("%d",a[i]);
       a = realloc(a,sizeof(int)*10);
       for ( i=5; i<10; i=i+1 ) a[i]=10-i;
       for ( i=0; i<10; i=i+1 ) printf("%d",a[i]);

       return 0;
    }
    复制到dev-c++后编译报错
    6 D:InformaticsprogramsDev-C++test.cpp invalid conversion from `void*' to `int*'
    9 D:InformaticsprogramsDev-C++test.cpp invalid conversion from `void*' to `int*'

    回复:这段C代码不能作为C++编译
    http://www.matrix67.com/blog/article.asp?id=291

  • axgle

    刚刚用vc试了一下,发现
    newNode = malloc( sizeof(struct node) );
    可以修改为
    newNode = (node *)malloc( sizeof(struct node) );
    否则:error C2440: '=' : cannot convert from 'void *' to 'node *'

    回复:嗯,事实上您的写法是标准的

  • 晓而不羽

    void swap(const int *a, const int *b )
    错了
    应该写成
    void swap(int* const a, int* const b )

    第一条指的是指针a,b是变量,但a,b指向的是“常量”,第二条指的是指针a,b本身是常量,但是a,b指向的是可变的变量。

  • clarkok

    其实pascal用 GetMem 和一些指针运算同样可以起到动态数组的效果,只不过用法上有些不同而已
    http://hi.baidu.com/clarkok/blog/item/ba8ea6dfa1660d0a62279823.html

  • clarkok

    其实pascal用 GetMem 和一些指针运算同样可以起到动态数组的效果,只不过用法上有些不同而已
    http://hi.baidu.com/clarkok/blog/item/ba8ea6dfa1660d0a62279823.html

  • 有奈了

    回10L,我认为你说的是对的,M67的不对,但我认为你表述不清,我从百度百科上摘抄一段,const的问题就明了了:
    int const nValue; // nValue是const   
    char const * pContent;// *pContent是const, pContent可变
    (char *) const pContent;//pContent是const,*pContent可变
    char* const pContent;// pContent是const,*pContent可变
    char const* const pContent;// pContent和*pContent都是const

  • cervelo

    定义一个指针的方法如下:
    类型名 *指针名;
    例如,下面的语句定义了一个指针:
    int *pointer;
    这样,pointer就是一个指针,它指向的是一个int类型的数据。
    一个指针可以是一个合法….

发表评论

  ×  2  =  8