Linux-Level1-day09:指针进阶;字符串;

3.7.常量,常量指针,指针常量,常量指针常量:围绕关键const(笔试题必考)
a)常量定义:不可修改的值,例如:250,'A’等
b)const关键字功能:常量化,四种形式:
1.const可以修饰普通变量,一旦修饰该变量就会被当成常量处理
即其值一经初始化再也不能改
例如:
const int a = 250;
a = 200; //gcc编译时会报错

2.常量指针:不能通过指针变量来修改指向的内存区域的值(保护内存区域不可乱改)
例如:
int a = 250;
const int *p = &a; //定义初始化一个常量指针
或者
int const *p = &a; //定义初始化一个常量指针

*p = 200; //gcc编译时会报错
printf(“%d\n”, *p); //可以,仅仅是读查看

int b = 300;
p = &b; //可以,指针变量p本身是可以修改的,p此时指向b
p = 400; //gcc编译时会报错
printf(“%d\n”, * p);//可以,仅仅是读查看
3.指针常量:指针永远指向一块内存区域,不能再指向别的地方(保护指针不可乱指向)
例如:
int a = 100;
int
const p = &a;
*p = 300; //可以,可以修改指向的内存区域
int b = 200;
p = &b; //不可以,gcc报错

4.const int * const p:常量指针常量,表示p本身不可修改,同时p指向的目标也不能修改
只能通过p来查看内存区域的值
例如:
int a = 100;
const int* const p = &a;
*p = 300; //不可以,可以修改指向的内存区域
int b = 200;
p = &b; //不可以,gcc报错

printf(“%d\n”, &p); //可以

/*const演示*/
#include 

int main(void)
{
    //1.常亮变量
    const int a = 250; //将变量常量化
   // a = 200; //gcc编译报错

    //2.常量指针
    int b = 250;
    const int *p = &b; //常量指针
    printf("b = %d\n", *p);
   // *p = 200; //gcc报错
    int c = 300;
    p = &c; //p指向c
    printf("c = %d\n", *p);
    //*p = 400; //gcc报错

    //3.指针常量
    int d = 20;
    int* const p1 = &d;
    *p1 = 200; //可以
    printf("d = %d\n", *p1);
    //int e = 40;
    //p1 = &e; //不可以,gcc报错

    //4.常量指针常量
    int f = 10;
    const int* const p2 = &f;
    printf("*p = %d p = %p\n",*p2,p2);//可以仅仅是读查看
   // *p2 = 100;//不可以
    int f1 = 10;
   // p2 = &f1;//不可以
    



    return 0;
}




3.8.无数据类型指针:void *
a)无数据类型指针(void *)概念:它也是一个指针变量,也保存一个地址,同样占4字节内存空间
只是它指向的内存区域的数据类型是不可知的
称这种指针为无数据类型指针,写法:void *
例如:
void *p = &a;

b)无类型指针特点:
1.通过此种指针变量是无法获知指向的内存区域保存的数据的数据类型
2.不能直接对无类型指针进行解引用操作"*",因为你不知道指向的内存区域的数据类型
也就不知道将来要取几个字节的数据,如果要想通过无类型指针获取内存的数据
必须做数据类型的转换(为了代码的可读性建议采用强制转换)
3.无类型指针void *加减几,实际的地址就是加减几
void *p = 0;
p++; //p=1
p++; //p=2
4.所有情况的案例代码
例如:
int a = 0x12345678;
void *p = &a; //虽然p指向a,但是通过p无法获取a变量的数据类型
*p = 300; //gcc编译报错,不清楚到底取几个自己的数据,没办法只能给你报错

  问:如何通过一个指针来获取a的数据呢?
  答:
  方式1:
         int a = 0x12345678;
         int *p = &a;
         printf("%#x\n", *p);
         虽然能够一次性获取4字节数据,但是现在不想一次性获取4个字节数据,想获取
         其中1个字节数据或者2字节数据或者4字节数据,显然这种方式做不到!
         要想实现这个功能,只需做以下方式的代码:
   方式2:通过有类型的指针变量来获取1字节,2字节
        获取1字节:
         int a = 0x12345678;
        //char *p = &a;  //隐式转换,代码可读性不高,gcc还要给个警告
        char *p = (char *)&a; //强制转换,提高代码的可读性,将&a的int类型转换成char*
        printf("%#x\n", *p++); //0x78
        printf("%#x\n", *p++); //0x56
        printf("%#x\n", *p++); //0x34
        printf("%#x\n", *p++); //0x12
         
        获取2字节:
         int a = 0x12345678;
        //short *p = &a;  //隐式转换,代码可读性不高 gcc还要给个警告
        short *p = ( short *)&a; //强制转换,将&a的int类型转换成short*
        printf("%#x\n", *p++); //0x5678
        printf("%#x\n", *p++); //0x1234
        
        结论:此方法虽然满足要求,但是如果做隐式转换,gcc老给一个警告
     问:能否将警告去掉呢?还有能够去掉指针变量++时跟类型相关问题呢?
     答:通过无类型指针void *实现
  方法3:通过无类型指针void *来获取1字节,2字节,4字节数据
   	获取1字节:   
      int a = 0x12345678;
  	  void *p = &a; //定义一个无类型指针指向a,无需强转,关键是gcc不给警告
printf("%#x\n", *(char *)(p+0)); //0x78,先将p强转为char *指针,然后取值
printf("%#x\n", *(char *)(p+1)); //0x56
printf("%#x\n", *(char *)(p+2)); //0x34
printf("%#x\n", *(char *)(p+3)); //0x12
或者
char *p1 = (char *)p; //将无类型指针强转为char*
printf("%#x\n", *p1++); //0x78
 printf("%#x\n", *p1++); //0x56
 printf("%#x\n", *p1++); //0x34
 printf("%#x\n", *p1); //0x12
        
  获取2字节:   
 int a = 0x12345678;
  void *p = &a; //定义一个无类型指针指向a,无需强转,关键是gcc不给警告
printf("%#x\n", *(char *)(p+0)); //0x5678
printf("%#x\n", *(char *)(p+2)); //0x1234
  或者:
short *p1 = (short *)p; //将无类型指针强转为char*
printf("%#x\n", *p1++); //0x5678
printf("%#x\n", *p1); //0x1234
#include 

int main(void)
{
    //0.无类型指针void
    void *pn = NULL;
   // printf("%d\n",*pn);
   // *pn = 10;
   printf("%p %p %p\n",pn,pn+1,pn+2);

    //1.通过有类型指针一次性获取4字节数据
    int a = 0x12345678;
    int *p = &a;
    printf("%#x\n", *p);

    //2.通过有类型指针来获取其中1字节,2字节数据
    char *p1 = (char *)&a; //把&a的类型int强转为char *指针
    printf("%#x\n", *p1++);//78
    printf("%#x\n", *p1++);//56
    printf("%#x\n", *p1++);//34
    printf("%#x\n", *p1);//12
    
    short *p2 = (short *)&a; //把&a的类型int强转为short *指针
    printf("%#x\n", *p2++);//5678
    printf("%#x\n", *p2);//1234

    printf("\n");

    //3.通过void *来获取1字节,2字节,4字节数据
    //1字节:
    void *p3 = &a;
    printf("%#x\n", *(char *)(p3+0));//78
    printf("%#x\n", *(char *)(p3+1));//56
    printf("%#x\n", *(char *)(p3+2));//34
    printf("%#x\n", *(char *)(p3+3));//12
    printf("\n");
   
    char *p4 = (char *)p3; //提高代码可读性,强转
    printf("%#x\n", *p4++);//78
    printf("%#x\n", *p4++);//56
    printf("%#x\n", *p4++);//34
    printf("%#x\n", *p4);//12
    printf("\n");
    
    //2字节
    short *p5 = (short *)p3;
    printf("%#x\n", *p5++); //0x5678
    printf("%#x\n", *p5); //0x1234
    printf("\n");
   
    //printf("%#x\n", *(short *)p3+0); //错了
    //printf("%#x\n", *(short *)p3+2); //错了
    printf("%#x\n", *(short *)(p3+0)); //0x5678
    printf("%#x\n", *(short *)(p3+2)); //0x1234
    //4字节
    int *p6 =(int *)p3;
    printf("%#x\n", *p6);//12345678
    printf("%#x\n", *(int *)(p3+0));//12345678
    return 0;
}








3.9.指针和函数的那点事儿(目前掌握指针和变量的那点事儿,指针和数组的那点事儿)
a)指针作为函数的形参
结果:函数通过指针能够访问操作指向的内存
参考代码:swap.c(实现两个数交换和设置某个位为1和清0)
例如:
void A(int *px, int *py, int *pm, int *pn)
{
//读查看
printf(“%d %d %d %d\n”, *px, *py, *pm, *pn);
//写修改
*px = …
*py = …
*pm = …
*pn = …
}

b)指针作为函数的返回值
结果:此类函数又称指针函数,也就是函数的返回值是一个指针,将来函数返回的是一个地址
跟返回的变量的内存生命周期相关,跟变量的使用范围无关
切记:千万要注意别返回一个野指针(笔试必考,结合四大类型的变量)
语法格式:返回值数据类型 *函数名(形参表){函数体语句}
例如:

//定义指针函数
int g_a = 250; //定义全局非静态变量
static int g_b = 520; //定义全局静态变量
int *A(void)
{
int a = 200; //定义局部非静态变量
static int b = 100; //定义局部静态变量
return &a; //返回一个野指针,因为变量a的内存随着函数的返回而消失
或者
return &b;  //可以,因为此内存一次分配终身使用
或者
return &g_a; //可以,因为此内存一次分配终身使用
或者 
return &g_b; //可以,因为此内存一次分配终身使用
}

int main(void)
{
int *p = A();
*p = 30000; //修改变量的值
printf(“%d\n”, *p); //查看指向的内存的数据
return 0;
}

#include 

int g_a = 100;
static int g_b = 200;

/*指针函数*/
int *A(void)
{
    int a = 300; //局部非静态变量
    static int b = 400;//局部静态变量
    printf("%d\n",b);
    //return &a; //返回野指针
    return &b; //可以,虽然他的范围只是在A函数,但是在main函数也能修改
    //return &g_a; //可以
    //return &g_b; //可以
}

int main(void)
{
    int *p = NULL;

    p = A();
    *p = 4;
    printf("%d\n", *p);
    A();
    return 0;
}

c)函数,指针,数组
函数访问数组的公式:
void A(int a[], int len); //其中a是数组的地址
等价于
void A(int *p, int len);

#include 

extern void print1(int a[], int len);
extern void print2(int *p, int len);
int main(void)
{
    int a[] = {1,2,3,4};
    int len = sizeof(a)/sizeof(a[0]);

    print1(a, len); 
    printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);

    print2(a, len); 
    printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);
    return 0;
}


void print1(int a[], int len){
	for(int i = 0;i < len;i++){
		a[i] *= 10;
	}
}

void print2(int *p, int len)
{
    for(int i = 0; i < len; i++) {
        p[i] += 2;
        *(p+i) += 2;
    }
    
}

4.C语言字符串相关内容
4.1.回顾字符常量:用单引号包含,例如:‘A’,‘B’,'1’等
实际内存存储的是对应的整型数,ASCII码
占位符:%c

4.2.字符串定义:由一组连续的字符组成,并且用""包含起来,并且最后一个字符必须是’\0’
此’\0’表示字符串的结束,此’\0’的ASCII码是0
'0’的ASCII为48
注意:研究字符串最终研究的就是里面的一个一个的字符
例如:“abcefg\0”

4.3.字符串特点
a)字符串的占位符:%s
printf(“%s\n”, “abc\0”); //直接跟字符串
或者
printf(“%s\n”, 字符串的首地址);
b)字符串占用的内存空间是连续的,并且每个字节存储一个字符
参见字符串内存图.png
注意:'\0’字符串的结束符如果后面还有内容,那么这些内容为无效的
例如:“abc\0efg\0”
printf(“%s\n”, “abc\0efg”); //abc
c)多个并列的字符串将来会由gcc帮你合并成一个字符串
“abc”“efg"合并成"abcefg”
printf(“abc”“efg\n”); //abcefg

4.4.字符串和指针的那点事儿
a)定义一个字符指针变量并且指向一个字符串,本质是指向这个字符串的首地址
也就是指向字符串中第0个字符的首地址(也就是字符a的首地址)
定义并且初始化字符串指针变量形式:
char *p = “abcefg”;
b)如果让一个字符指针变量指向一个字符串,此字符串无需跟’\0’,gcc将来帮你添加’\0’
d)切记:不能通过字符指针变量来修改字符串的值,只能查看(笔试题必考)
//读查看
printf(“%s\n”, p);
*(p + 2) = ‘C’; //目标将第2个字符c变成C,做不到,报错

/*字符串演示*/
#include 
int main(void)
{
    //1.直接打印字符串
    printf("%s %s\n", "abc\0efg","abcdefg");

    
    //2.字符串合并
    printf("abc""efg\n");

    //3.指针形式的字符串
    char *p = "abcefg";
    printf("%s\n", p);
    //*(p + 2) = 'C'; //将第2个字符c修改为C,结果程序崩溃了,不可修改

    //4.数组形式的字符串
    char a[] = {'x', 'y', 'z', '\0'};
    printf("%s\n", a); //打印字符串
    a[2] = 'Z'; //修改元素
    printf("%s\n", a); //打印字符串
    
    char b[] = "hjkl";
    printf("%s\n", b); //打印字符串
    b[2] = 'K'; //修改元素
    printf("%s\n", b); //打印字符串
    printf("sizeof(b) = %ld\n", sizeof(b)); //5,'\0'也算一个字符
    return 0;
}

4.5.字符串和数组的那点事儿,两种写法:
a)写法1:
char a[] = {‘a’, ‘b’, ‘c’, ‘\0’};
注意:如果想把a当成字符串,需要手动最后添加’\0’,如果不添加a仅仅就是一个包含三个元素
的数组,而不是有效字符串
b)写法2:
char a[] = “abc”;
注意:无需添加’\0’,gcc编译器将来帮你追加一个’\0’

c)切记:不管是哪种写法,字符数组中的元素是可以修改的(笔试题必考)
例如:将第2元素’c’修改为’C’:
a[2] = ‘C’;
或者
*(a + 2) = ‘C’;

4.6.笔试题必考题目:编写一个字符串比较函数my_strcmp
参考代码:strcmp.c

4.7.标准C语言提供的字符串操作函数(都是大神写好的,咱直接调用即可)
坚定信念:自己实现一个字符串操作函数完全没问题!不像scanf与printf函数咱们现在写不出来。
如果要用以下大神写好的字符串操作函数,需要添加头文件:#include

#include 
int my_strcmp(const char *p1, const char *p2)
{
    while(*p1) {
        if(*p1 != *p2)
            return *p1 - *p2;
        p1++;
        p2++;
    }
    return *p1 - *p2;
}
int main(void)
{
    int ret = 0;
  
  
    char *pstr1 = "hello,world"; 
    char *pstr2 = "hello,china";

    ret = my_strcmp(pstr1, pstr2);
    if(ret == 0) 
        printf("pstr1和pstr2相等\n");
    else if(ret < 0)
        printf("pstr1小于pstr2\n");
    else if(ret > 0)
        printf("pstr1大于pstr2\n");
    return 0;
}

a)strlen函数:功能获取字符串有效长度(不包括’\0’)
例如:printf(“长度:%d\n”, strlen(“abc”)); //3
或者
char *p = “abc”;
printf(“长度:%d\n”, strlen§); //3

b)strcat函数:功能是字符串拼接
例如:
char a[10] = “abc”;
char *p = NULL;
p = strcat(a, “xyz”); //把xyz拼接在abc的后面保存到数组中
printf(“%s %s\n”, p, a); //abcxyz abcxyz

c)strcmp函数:功能是字符比较函数
例如:
char *p1 = “abc”;
char *p2 = “xyz”;
int ret = strcmp(p1, p2);
int ret = strcmp(“abc”, “xyz”); //本质最终传递的是字符串的首地址

d)strcpy:字符串拷贝函数,会覆盖原先的字符串
例如:
char a[10] = “abc”;
char *p = NULL;
p = strcpy(a, “xyzmn”);
printf(“%s %s\n”, p, a);

e)sprintf(游哥最爱):功能把数字(250)转成字符串"250"保存到数组中
例如:
char a[50] = {0};
sprintf(a, “%d %g, %c”, 250, 250.2, ‘A’);
printf(“%s”, a);

#include 
#include 

int main(void)
{
    //1.strlen
    char *p1 = "abc";
    printf("%ld %ld\n", strlen(p1), strlen("abc")); //求有效字符串个数

    //2.strcat
    char a[10] = "abc";
    char *p = NULL;
    p = strcat(a, "xyz");
    printf("%s   %s\n", p, a);
    printf("%ld\n", sizeof(a)); //10
    printf("%ld\n", strlen(a)); //6

    //3.strcmp
    int ret = 0;
    ret = strcmp("abc", "abd");
    printf("%d\n", ret);
    ret = strcmp("abc", "abc");
    printf("%d\n", ret);
    ret = strcmp("abc", "aba");
    printf("%d\n", ret);

    char *p2 = "abc";
    char *p3 = "abd";
    ret = strcmp(p2, p3);
    printf("%d\n", ret);

    //4.strcpy
    char b[10] = "abc";
    char *p4 = NULL;
    char *p5 = "hello";
    p4 = strcpy(b, "xyzmn"); //把字符串xyzmn拷贝到b数组中
    printf("%s %s\n", p4, b); //p4=b,p4指向数组b
    p4 = strcpy(b, p5); //把字符串hello拷贝到b数组中
    printf("%s %s\n", p4, b); //p4=b,p4指向数组b
    
    //5.sprintf:格式化输出函数
    char c[50] = {0};
    sprintf(c, "%d%g%c%#x ", 250, 250.2, 'A', 250);
    printf("%s\n", c);
    sprintf(c,"abc defg");
    printf("%s\n",c);
    return 0;
}











你可能感兴趣的:(linux)