C语言学习笔记(1)——指针(上)

来源:互联网 发布:淘宝订单售后流程图 编辑:程序博客网 时间:2024/06/10 21:17

之前课程都没有做记录。发现基本学完就忘。所以现在在博客记录下学习的过程。以及一些基本概念。

int *p; //定义了一个可以执行int类型地址的指针变量,名字叫pp=&a ;// 把a的内存地址复制给p*p=10//通过指针变量间接的访问a的值,*p代表指针指向变量的值,p代表指向变量的地址char *p1=&a;//两个类型不相同的地址 会出现warning 为了避免可以用强转(不要这样做,不兼容)char *p1=(char*)&a;会导致a的值发生不可预料的变化(强转只是语法上对了。实质是不对的)void *p2 ;可以指向任意类型的地址//一个地址编号对应的是一个BYTE的空间大小//一个int四个BYTE,占了4个地址编号//地址编号在32位系统下,是一个4个字节的无符号整数,在64位系统下是一个8个字节的无符号整数//程序中不要出现野指针,但可以出现空指针!!!{    int *p;//没有初始化过值的指针,称为野指针    *p=100; } {     int *p;     p=NULL//如果一个指针变量没有明确的指向一块内存,那么就把这个变量指向NULL,这个指针就是空指针     #define NULL 0 //NULL在C语言是一个宏常量,值为零 }const int a=100;//c语言中的const是有问题的,因为可以通过指针变量间接的修改const常量的值,所以在c语言中用#define常量的时候更多,在C++中没有这个问题,const的值不会被改。const int *p=&a;//p是一个变量,但指向一个常量,p可以指向一个int类型的地址,但不可以用*p的方式修改这个内存的值。int *const p;//p是一个常量,但指向一个变量或者常量,可以通过这个*p读写这个内存的值{    int a[10]={1,2,3,4,5,6,7,8,9,0};    int *p;    p=a;//这是合法的,数组的名字代表它的首地址    p[3]=100;//当指针变量指向一个数组的时候,C语言语法规定指针变量名可以当数组名使用    printf("%lu,%lu\n",sizeof(a),sizeof(p));//这里就有区别了,前面sizeof(a)是这个数组的长度为40 后面sizeof(p)在64位系统下为8    p=&a[2];//这也是合法的,注意此时p[3]指向了a[5]}char[100]="helloworld!"//定义了一个长度为100个char的数组,同时初始化数组成员变量const char *s="helloworld!"//定义了一个字符串常量,s指向这个常量的首地址,因为指向一个常量,为了严谨用const同时:a[0]='a'//合法的,因为数组的所有成员都是变量s[0]='a'//非法的,因为s指向的是一个常量 如果函数参数是一个数组,那么函数内部是不知道这个数组成员数量的,所以需要额外加一个参数,说明字符数量。如果是一个字符串不用,因为字符串以/0结尾。//指针变量可以计算,如果int *类型加一,变化4个整数,如果char *加一,变化1个整数{    int a=0;    int *p=&a;    printf("%p,%p,%p\n",p,p+1,p+2);//由于为int 所以输出的三个地址之间差值为4    但是注意这是输出地址的改变值    a1[10]={0};    int *p1=a;    *p1+=5;    *p1=1;//这里a[5]的值变为5    //p[3] 和*(p+3)是等价的。}{    int a=0x12345678;    char *p=(chao *)&a;    print("%x\n",*p);//此时小端对齐输出78 大端对齐12    print("%x\n",*p,p[1]);//小端对齐输出78 和56    *p=0;//此时再输出a的值为0x12345600    //c语言中所有的数据类型都可以理解为一个char的数组,在这里简单的理解就是甚至可以使用p[3]=0.此时输出a的值为345678这里理解一下(加深一下char字符串中出现零代表字符串结束的印象,数组出现0不会结束!)。    char b[20]={0};//还可以使用这种极端的例子    int *p1=(int *)b;   }ip地址其实是个无符号的整数unsigned int0.0.0.0255.255.255.255正好是四个char直接用unsigned char *p=(unsigned char *)&a;//这时输出p[i]会输出ip地址/逆序的ip地址把ip地址化为整数的程序:{    char a[]="192.168.1.1";    unsigned int ip=0;    unsigned char *p=(unsigned char *)&ip;    int a1,a2,a3,a4;    sscanf(a,"%d.%d.%d.%d",&a1,&a2,&a3,&a4);    p[0]=a4;    p[1]=a3;    p[2]=a2;    p[3]=a1;    printf("%u\n",ip);}对于数组,用数组可以大大简化:char a[2][5]={{4,3,2,1,5},{7,15,25,64,74}};char *p=(char *)a;int i,j;for(i=0,i<10;i++){    for(j=1;j<10-i;j++)    {        if(p[j]<p[j-1])        {            char tmp =p[j];            p[j]=p[j-1];            p[j-1]=tmp;        }    }}for(i=0;i<2;i++){    for(j=0;j<5;j++)    {        printf("%d/n",a[i][j]);    }}//指针数组char *a[10];//定义了一个指针数组,每个成员是char*类型,共十个成员int *b[10];定义了一个指针数组,每个成员是int*类型,此时sizeof(a),和sizeof(b)在64位系统下都是80!!!int i=0;a=&i;//这里是非法的,因为a是个数组的名字,数组名不能做左值b[0]=&i;//这个才是合法的sizeof(b[0]) sizeof(*b[0])分别为8 4因为前一个为地址长度,后面为一个int4指针数组做main的形参int main(int argc ,char **args)//内部使用用*args[i]//例如 做一个加减乘除的程序 a 5+6 这里包括程序名共四个字符串,用int b=atoi(args[1]);int c=atoi(args[3]);//将5 6转化为int 而+号并不是'+'而是"+" 所以用char *s=args[2]//得到第二个参数,因为每个参数的类型都是char * char c=s[0]//将加号提取出,也就是得到char数组中得到的第一个成员变量的值char c=args[2][0]//一般不用上面两行这样的形式写,现在这种才是比较常见的但是*号时有问题,因为linux中*是个通配符,所以在输入时,用/*当做乘就可以解决这个问题。//二级指针int a=0;int *p = &a;int **pp;PP=&p;**pp=10;//次数a变为10//函数的参数是指针变量C语言想通过函数内部修改实参的值,只能给函数传递实参的地址来间接修改实参的值,这就是为什么scanf里面要用&a &b等,scanf要改变a的值,只能用传递a的地址的方式间接改变a的值//把数组名做函数参数此时,数组内的值会改变,这是因为当一个数组名作为函数的形参的时候,其实是一个指针变量名void test(int a[10])void test(int a[])void test(int *a)//以上三种是一样的,所以一般写成第三种void test(const int *a)//这样写是为了不让函数内部修改数组成员的值,但是可以通过强转用另外一个地址指向*a,然后通过另外一个地址修改数组成员的值,也就是说C语言中的const是一直不安全的{   printf("%lu\n",sizeof(a));//这里输出8a[5]=50;}int main(){    int a={0};    printf("%lu/n",sizeof(a));//这里输出40    test(a);//数组内部值改变了}可以定义 int *test()//函数的返回类型是指针类型

下面来看看C语言中的几个库函数memset memcpy memmove
memset是将指定区域的内存置空 int a[10]={1,2,3,4,5,6,7,8,9,0}; 此时不能用a[10]={0},只能用for循环一个个将a[10]中的值清空,很麻烦,所以就要用到memset 调用memset需要头文件#include