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

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

int *p; //定义了一个可以执行int类型地址的指针变量,名字叫p
p=&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 int
0.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));//这里输出8
a[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

猜你喜欢

转载自blog.csdn.net/qq_21747841/article/details/78196257