`

C语法总结 数组和指针

 
阅读更多

 

数组

#define N 10
int a[N];
for(int i=0;i<N;i++) {
    a[i] = 0;
}

//初始化,没有被初始化到的数组下标就被设置为0
int a[10] = {1,2,3,4,5};
//等价于
int a[10] = {1,2,3,4,5,0,0,0,0,0};
//也可以写成入如下形式相当于把整个数组全部赋值为0
int a[10]={0};

//C99中可以指定对某个元素下标赋值,剩余的就被赋值为0
int a[15] = {[2]=11, [9]=33, [14]= 99};

//对数组使用sizeof运算符
int a[10];
sizeof(a);    //结果就是40
//sizeof 返回的是无符号的整形size_t,无符号和有符号之间运算会有危险,
//最好使用强制转型

//数组清零
#define SIZE   ( (int)(sizeof(a)/sizeof(a[0])) )

//多维数组初始化
int m[5][9] = {{1,2,3,4,5,6},
                        {2,3,54,5,6},
                        {4,5,6,7,8},
                        {3,5,6,7,11}};
//也可以省略外面的一层大括号
int m[5][9]  : {1,2,3,4,5,6,
                        {2,3,54,5,6,
                        {4,5,6,7,8,
                        {3,5,6,7,11};

//常量数组
const char hex_chars[] = {'0','1','2','3'};

//C99中也可以对多维数字进行指定下标的初始化
//C99还支持变长数组

 

 

 

指针

//定义一个指针
int *p;
//每个指针变量只能指向一种特定的类型
int *p;
double *d;
char *r;

//赋值和访问  &运算符是获取一个变量的地址值,  *那个运算符是间接寻址
int i, *p;
i = 10;
p = &i;
printf("%d",*p);

//这样的赋值是错误的
int i, *p;
i = 10;
*p = &i;    //这里是将 i的地址赋予p指向的内容,所以是错误的
                //*p是指针指向的内容,是一个具体的值,p才表示一个地址,所以
                //应该是 p = &i;  将i的地址赋予指针p才是正确的

//指针作为返回值
int *max(int *a, int *b) {
    if(*a > *b) {
        return a;
    }
    return b;
}
//注意,永远不要返回指向局部变量的指针
int *f(void) {
    int i;
    return &i;
}
//一旦函数f返回,变量i就不存在了,所以变量i的指针将是无效的,有的编译器会
//在这种情况下给出类似"function returns address of local variable"的
//警告



//未初始化的指针变量指向的是一个不确定的内存地址值
//用const保护参数,也就是指针指向的内容是常量不可改变,但指针内容本身是
//可以被改变的,也可以包含指针本身,还可以包含指针本身和指针指向的内容
//第一种情况,保护指针指向的内容,此时指针本身可以改变
int number = 100;
const int *p;
//*p = 0;
p = &number;
       
//第二种情况,保护指针本身,指针指向的内容可以被改变
int *const p2;
*p2 = 0;
//p2 = &number;
       
//第三种情况,包含指针本身,以及指针指向的内容
const int *const p3;
//*p3 = 0;
//p3 = &number;

  

 

 

 

指针和数组

//定义指针,指针p指向a数组的第一个元素
int a[10],  *p;
p = &a[0]

//设置数组a有10个元素
p = &a[2];           //指向数组a的第三个元素
int *q = p+3;      //指向数组a的第六个元素,也就是a[5]
p -= 4;                //指向数组a的第二个元素,也就是a[1]

//两个指针之间可以相减,但是两个指针必须都指向同一个数组,因为比较的是内存
//地址,如果两个数组指向的是两个不同的数组,最后计算出来的结果可能是未知的
//内存

 *运算符和 ++运算符(++运算符优先级高于*)

表达式 含义
*p++或者*(p++) 先自增p的值,然后再获得*p
(*p)++ 自增前的表达式是*P,以后再自增*p
*++p或*(++p) 先自增p,自增表达式的值是*p
++*p或++(*p) 先自增*p,那个自增后表达式的值是*p

 

//用数组名做指针
int a[10];
*a = 7;   //等于a[0] = 7;
//也可以通过指针来修改a[1]
*(a+1) = 12;    //等于a[1] = 12;
//遍历
for(p=&a[0]; p<a[N]; p++) {
    sum += *p;
}

//处理多维数组
int a[NUM_ROWS][NUM_COLS];
int *p;
for(p=&a[0][0]; p<= &a[NUM_ROWS-1][NUM_COLS-1]; p++) {
    *p = 0;
}

//C99中的指针和变成数组

 

 

 

指针的高级应用

//内存分配函数,这些都是声明在<stdlib.h>头文件中
//malloc()函数      分配内存块,但是不对内存块进行初始化
//calloc()函数       分配内存块,并且对内存块进行初始化
//realloc()函数     调整先前分配的内存块大小
//空指针用表示   不指向任何地方的指针,用名为 NULL  的宏来表示
//NULL 定义在<stdlib.h>中
p = malloc(1000);
if(NULL == p) {
     //分配失败
}

//malloc函数原型,它返回的是void*类型指针,所以需要转型,这个void*就代表
//任何类型
void *malloc(size_t size);
//分配字符串时需要分配n+1大小空间,因为字符串最后是\0结尾
p =(char*) malloc(n+1);
//分配数组空间,注意分配时必须使用sizeof计算单个值占多少字节
int *a;
a = malloc(n * sizeof(int));

//calloc函数原型,为nmemb个元素的数组分配内存空间,其中每个元素长度
//都是size个字节,如果要求的空间无效会返回空指针
void *calloc(size_t nmemb, size_t size);
a = calloc(n, sizeof(int));

//realloc函数原型,其中ptr必须指向先前通过malloc,calloc,realloc的调用
//获得的内存块,size表示新尺寸可能会大于或小于原有尺寸。
void *realloc(void *ptr, size_t size);

 C标准列出了几条关于realloc函数的规则

1.当扩展内存块时,realloc函数不会对添加进内存块的字节数进行初始化

2.如果realloc函数不能按要求扩大内存块,那么它会返回空指针,并且在原油的内存块中的数据不会发生改变

3.如果realloc函数调用时以空指针作为第一个实际参数,那么它的行为就像malloc函数一样

4.如果realloc函数被调用时以0作为第二个实际参数,那么它会释放掉内存。

注意:一旦realloc函数返回一定要对所有指针进行更新,因为ralloc函数可能会使内存块移动到了其他地方

//用free函数释放动态申请的内存,其原型为
void free(void *prt);
//悬空指针问题
char *p = malloc(4);
free(p);
strcpy(p, "abc");      //此时p指向的内存已经被释放掉了,再对其修改可能会造成
//严重的错误

//结构体
struct node {
    int value;
    struct node *next;
};
struct node *new_node= NULL;
new_node = malloc(sizeof(struct node));
//写成这样是不对的,我们需要申请结构体大小的空间,而这样申请的是指针大小的
//空间,一个指针可能是4个字节,这样实际上就申请了4个字节
new_node = malloc(sizeof(new_node));

//  ->运算符
(*new_node).value = 10;
//等价于
node_new->value = 10;

//指向指针的指针
char **ptr;
*ptr = "abc";    //ptr是指向"abc"的地址


//指向函数的指针
//假设函数integrate的原型如下: (*f)说明f 是个指向函数的指针
double integrate(double (*f)(double), double a, double b);
//也可以写成:
double integrate(double f(double), double a, double b);
//调用integrate时,将把一个函数名作为第一个实际参数,注意sin后面没有括号
//C编译器会产生指向函数的指针而不是产生函数调用的代码
result = integrate(sin, 0.0, PI/2);
//在函数integrate中, *f 表示调用f 所指向的函数,*f 就是调用sin函数
// (*f)(x) 和f(x)都是一样的,但是用(*f)(x)更好,说明是f是指向函数的指针


//C函数库中的qsort函数原型
void qsort(void *base, size_t nmemb, size_t size,
        int (*compar)(const void*, const void*));
//base是指向数组中的第一个元素,nmemb是要排序元素的数量,size是每个
//数组元素的大小,compar是指向比较函数的指针

int compare_parts(const void *p, const void *q) {
    const struct part *p1 = p;
    const struct part *p2 = q;
    if(p1->number < p2->number) {
        return -1;
    }
    else if(p1->number == p2->number) {
        return 0;
    }
    return 1;
}
//还可以写成:
int compare_parts(const void *p, const void *q) {
    return ((struct part*)p)->number - ((struct part*)q)->number;
}

//指向函数的指针,可以指向任何参数是int,返回void类型的函数
void (*pf) (int);
void fun(int x);
pf = fun;       //让pf指向函数fun,注意fun前面不需要加 &
pf(100);


//受限指针
int * restrict p;
//灵活数组成员
struct vstring{
    int len;
    char chars[N];
};
//这里N表示一个字符串最大长度的宏,但是分配最大长度会导致浪费,于是采用
//如下方式申明chars长度
struct vstring{
    int len;
    char chars[1];
};
struct vstring * str = malloc(sizeof(struct vstring) +n-1);
//这样就看以达到目的了,并且内存不会浪费,这样叫 struct hack 技术
//C99支持灵活数组成员,当结构体最后一个是数组时可以省略长度
struct vstring{
    int len;
    char chars[];
};


 

定义NULL为

#define NULL (void*)0;

 如果把NULL 定义为0,那么把NULL赋给整型变量index是合法的,但是把NULL定义为(void*)0 则表示把指针赋值给了整型变量

 

将malloc函数的值类型强制转换没有任何好处,这是来自于经典C的习惯。

 

 

 

 

 

参考:

理解一般指针和指向指针的指针

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics