C语言 DAY09 指针02

1.指针的指针

又名二维指针

语法:数据类型  **p;

示例:        

        void fun()
        {
                int a=10;
                int *p1 = &a;
                int **p2 = &p1;
                printf("p1的地址是:%p\n",p1);
                printf("p2的存储的地址是:%p\n",*p2);
                printf("p2的地址是:%p\n",p2);
        }

2.const与指针

1.指针常量

概念

        不能改变其指向变量的地址, 即地址将保持不变如果一个常量指针指向某个变量, 那么它不能指向其他变量 ,但是可以该指针修改其指向的地址中的数据 ,其本质是一个常量

语法

        数据类型   * const  指针变量名

示例

void fun()

{

int num = 10;

int num1 = 5;

int *  const p = #

// 指针常量不能修改其指向的地址
//p = &num1;

*p = 100;//可以修改值

printf("num=%d\n",*p);

}

2.常量指针

概念      

        指向常量的指针,本质是一个指针,不能通过指针改变其指向的地址中的数据,但是可以改变其存储的地址

语法

        const  数据类型 * 指针变量名  or    数据类型  const  *  指针变量名

示例      

void fun()

{

int num = 10;

int num1 = 5;

const int *  p = #

// 指针常量能修改其指向的地址
p = &num1;

//*p = 100;//不可以修改值

printf("num=%d\n",*p);

}

3.常量指针常量

概念

        指向的地址与指向的地址中的数据都不能被修改

语法

        const  数据类型 * const  指针变量名

        数据类型 const  * const  指针变量名

示例

        

void fun()

{

int num = 10;

int num1 = 5;

const int * const  p = #

// 指针常量不能修改其指向的地址
//p = &num1;

//*p = 100;//不可以修改值

printf("num=%d\n",*p);

}

4.注意

        以上名称在网上有所不同,以上为主流说法

3.指针与数组元素

概述

        数组,是多个相同类型的变量的集合, 每个变量都占内存空间,都有地址编号 ,指针变量当然可以存放数组元素的地址

注意

       只有两个相同类型的指针指向同一个数组的元素的时候,比较大小才有意义 ,指向前面元素的指针小于指向后面元素的指针

示例

        

void fun04()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,0};
    int *p = &nums[3];
    printf("%d\n",*p);
//因为数组在内存中是连续开辟的
//所以我们可以通过对指针的加减使其指向数组中不同位置的元素
    printf("%d\n",*(p+1));
    printf("%d\n",*(p+2));
    printf("%d\n",*(p-1));
    printf("%d\n",*(p-2));
//指针变量存储的地址编号,地址编号是一个十六进制的数
    printf("%p\n",&nums[0]);
    printf("%p\n",&nums[1]);
//因为地址编号是一个十六进制的数所以我们可以对其使用关系运算符
    if(&nums[0] < &nums[1])
    {
    printf("nums[0]在前\n");
    }
    else
    {
    printf("nums[1]在前\n");
    }
//两个相同类型的地址相差,使用十六进制数相差之后得到的数值 除以其数据类型的字
节大小,为最终结果
    printf("%ld\n",&nums[0] - &nums[1]);
}

4.指针与数组

概述

        

1, 数组名其实是数组中首元素的地址
2, 数组名可以赋值给一个指针变量 , 此时该指针指向一个数组 , 被称为数组指针 , 其本质是一个指针
指向一维数组指针的语法
        数据类型 * 指针名 ;
指向二维数组指针的语法
        数组类型 (* 指针名 )[ 二维数组中一维数组的长度 ];
指向三维数组指针的语法
        数组类型 (* 指针名 )[ 三维数组中二维数组的长度 ][ 二维数组中一维数组的长度];
3, 数组名本质上是一个指针常量 , 顾其指向的地址无法进行修改
4, 字符数组与字符串指针的区别
字符数组 : 在内存(栈、静态全局区)中开辟了一段空间存放字符串 , 将其首元素地址赋值给数组名,是个指针常量
字符串指针:
如果指向在文字常量区中存放字符串,会将字符串的首地址付给指针变量,此时该指针是个常量指针
如果指向栈、静态全局区中存放的字符数组 , 那么则是一个普通的指针变量
5, 指针变量是一个变量 , 数组可以存储多个类型相同的变量的容器 , 顾数组可以存储多个指针变量, 此时该数组称为指针数组 , 其本质是一个数组

语法

        数据类型 * 数组名 [ 长度 ];
示例:
void fun05()
{
//指针与一维数组
int nums[10] = {1,3,5,7,9,2,4,6,8,0};
//数组名其实是数组中首元素的地址
printf("%p\n",nums);
printf("%p\n",&nums[0]);
//数组名可以赋值给一个指针变量
int * p1 = nums;
char str[] = "hello";
char * p2 = str;
//此时我们可以理解为指针变量就等价与其指向的数组的数组名
//顾我们可以通过指针变量名[下标]获取其指向的数组中指定位置的元素
printf("%d\n",p1[1]);
printf("%c\n",p2[1]);
//指针与二维数组
int numss[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int (*p3)[5] = numss;
printf("%d\n",p3[1][2]);
char strs[5][50] = {"hello","world","c++","python","c"};
char (*p4)[50] = strs;
printf("%c\n",p4[1][2]);
printf("%s\n",p4[1]);
}
//遍历数组元素
void fun06()
{
int nums[10] = {1,3,5,7,9,2,4,6,8,0};
//数组名就是数组第一个元素的的地址
int *p = nums;
for(int i = 0; i < 10; i++)
{
// printf("num[%d] = %d\n",i,*(p + i));
// printf("num[%d] = %d\n",i,*(nums + i));
// printf("num[%d] = %d\n",i,*(p++));
//注意数组的变量名是一个指针常量
//printf("num[%d] = %d\n",i,*(nums++));
}
}

void fun07()
{
char str01[] = "hello";
char *str02 = "hello";
//指针常量能修改其指向地址的元素,但是不可以修改其指向的地址
str01[0] = 'H';
printf("str01=%s\n",str01);
//会产生段错误
//常量指针不能修改其指向地址的元素,但是可以修改其指向的地址
//str02[0] = 'H';
//printf("str01=%s\n",str02);
str02 = "world";
函数与指针
printf("str02=%s\n",str02);
//使指针变量指向栈中的字符数组
char *str03 = str01;
str03[0] = 'H';
printf("str03=%s\n",str03);
str03 = "world";
printf("str03=%s\n",str03);
}

void fun08()
{
int a = 1,b = 2,c = 3;
int * p[] = {&a,&b,&c};
printf("%d\n",*(p[0]));
printf("%d\n",*(p[1]));
printf("%d\n",*(p[2]));
}

5.函数与指针

概述

        

1, 函数名本身就是函数在代码区存储该函数的地址 , 顾可以赋值给一个指针变量 , 但是因为其在代码区的地址不可修改, 顾该指针是一个指针常量
语法 :
        函数指针的定义与初始化
        返回值类型 (* 指针名称 )( 指向的函数的形参列表的数据类型 ) = 函数名;
注意 :
        指向的函数的形参列表的数据类型可以忽略不写
调用其指向的函数
        指针名( 实参列表 );
        变量名 = 指针名 ( 实参列表 );
注意 :
        实参列表可以忽略不写
2, 指针变量是一个变量 , 顾可以作为函数的形参 , 此时传递的是指针变量的地址
3, 字符串指针作为实参 , 将指针指向常量区的内容传递函数。函数内部修改指针内容时 ,不会影响函数外部的值。
4, 将字符指针的指针作为函数的实参 , 函数的形参使用 **q( 指针的指针 ), 函数内部可以修改字符指针地址, 即可以函数外部的结果
5, 数组作为实参传递可以用指针变量接收 , 传递的是地址
6, 字符指针数组作为实参时,函数的形参的写法: 1) char *q[] 2)
7, 函数中局部指针变量做为函数的返回值, 函数执行完毕后 , 其局部指针变量指向的地址也将被释放, 外部无法使用 . 导致段错误
如需外部使用可以将局部指针变量修改为静态局部指针变量

示例

void test01()
{
printf("test01函数被调用\n");
}

void test02(int a,int b)
{
printf("test02函数被调用\n");
}

void test03(int a,int b)
{
printf("test03函数被调用\n");
printf("a=%d\n",a);
printf("b=%d\n",b);
}

int test04(int a,int b)
{
printf("test04函数被调用\n");
printf("a=%d\n",a);
printf("b=%d\n",b);
return a+b;
}

void fun09()
{
void (*p1)() = test01;
p1();
void (*p2)() = test02;
p2();
void (*p3)(int,int) = test03;
p3(10,12);
int (*p4)(int,int) = test04;
int num = p4(10,12);
printf("num=%d\n",num);
}

void changNum(int *p)
{
*p = 20;
}

void fun10()
{
int num = 10;
changNum(&num);
printf("num=%d\n",num);
}

void changName(char *p)
{
p = "高三石";
}

void fun11()
{
char *name = "高磊";
changName(name);
printf("name=%s\n",name);
}
void changName02(char **p)
{
*p = "高山石";
}

void fun12()
{
char *name = "高磊";
changName02(&name);
printf("name=%s\n",name);
}
void set(int*nums)
{
nums[0] = 10;
}

void fun13()
{
int nums[5] = {0};
printf("set 前 nums[0]=%d\n",nums[0]);
set(nums);
printf("set 后 nums[0]=%d\n",nums[0]);
}
void getName01(char * ns[],int i)
{
printf("%s\n",ns[i]);
}
void getName02(char ** ns,int i)
{
printf("%s\n",*(ns+i));
}
void setName(char ** ns,int i,char * newName)
{
*(ns+i) = newName;
}

void fun14()
{
char * names[3] = {"c","c++","sql"};
getName01(names,1);
getName02(names,2);
char * name = "cplusplus";
setName(names,1,name);
getName01(names,1);
}
int *getNums01()
{
int nums[3] = {1,2,3};
return nums;
}
int *getNums02()
{
static int nums[3] = {1,2,3};
return nums;
}

void fun15()
{
//int *p01 = getNums01();
//printf("%d\n",*(p01+1));
int *p02 = getNums02();
printf("%d\n",*(p02+1));
}

6.指针名称总结

野指针
        局部变量定义的指针没有赋初值
        如:
        int *p;
空指针
        指针的值为NULL
        如:
        int *p = NULL;
空类型指针 ( 万能指针 )
        指针类型为void * 的指针
        如:
        int num = 0;
        void *p = #
指针的指针
        存储指针地址的指针
        如:
        int num = 0;
        int *p = #
        int **p2 = &p;
指针常量 本质是一个常量 , 该指针变量不能修改指向的地址 , 但是可以修改地址中的值
        如:
        int num = 0;
        int * const p = #
常量指针
        本质上是一个指针, 指向常量 , 所以可以修改其指向的地址 , 但是不能修改其指向地址的值
        如:
        const char *p = "name";
        常量指针常量
指针常量与常量指针的结合体 , 既不能修改地址 , 也不能修改值
        如:
        const char * const p = "name";
数组指针
        指向数组的指针, 本质是一个指针
        如:
        一维数组指针
        int *nums;
二维数组指针
        int (*nums)[5];
指针数组
        本质是一个数组, 存储的元素的数据类型为指针
        如:
        int a=1,b=2,c=3;
        int *nums={&a,&b,&c};
        char *names[3] = {"hello","world","c"};
函数指针
        本质是个指针, 存储函数在代码区的地址
        如:
        void test(){
        }
void (*p)();
        返回值为指针的函数声明
        声明一个返回值为指针的函数
        extern int *test();
        int *test()
        {
        static int num = 10;
        return #
        }

你可能感兴趣的:(C语言,c语言,开发语言)