c语言基础3

目录

1.指针

1)一般形式:

2)&和*

3)指针的初始化

2 、指针的运算

1.指针的关系运算

3.指针的赋值运算

2.指针与二维数组

1.行指针(数组指针)

2字符指针

3.指针数组

3.const

4.void

1.函数


1.指针

在计算机内部,每一个字节单元,都有一个编号,称为地址。

内存的地址称为指针,专门用来存放地址的变量称为指针变量

        

地址:按字节为单位,进行编号;

指针:内存单元的地址;

指针变量:用来保存(某个变量的)地址的变量;

指针的目标变量:指针指向的变量;

变量是对程序中存储空间的抽象

1)一般形式:

<存储类型> <数据类型> *<指针变量名>

存储类型:指针变量自身的存储类型,默认为自动存储;

数据类型:指针指向的目标的数据类型

(由所指向的元素的数据类型决定);

*

指针变量名:合法的标识符;

指针的数据类型可以看做 存储的地址所指向的元素的数据类型+ *

eg: int a=0;

int *p=&a;

p的数据类型可以看做(int *)

eg:

int *p;//表示一个可以保存整形变量地址的指针

&p --->表示指针变量自身的地址,和目标变量没关系

指针所占字节数和操作系统有关,和目标变量无关;

32位操作系统的指针变量占4字节,64位操作系统的指针变量占8字节

2)&和*

&:取一个变量的地址(在一个变量的基础上取地址)

        找到你的地址

*:取一个指针指向的变量的值(在一个地址的基础上取值)

        顺着地址过去找你

&和*互为 逆运算

空指针:指针存放零值(缺省属性)

eg: int *p=NULL;

野指针:一个没有指向的指针变量(没有储存地址的指针变量),操作野指针会段错误(访问非法内存)

eg: int *p;

p=100;

3)指针的初始化

<存储类型> <数据类型> *<指针变量名>=<地址量>
eg:
    int var=10;
    int *p=&var;(赋初值)

    int var=10;
    int *p;
    p=&var;(赋值)

2 、指针的运算

(指针的类型一样才进行关系运算或算术运算,类型不同运算无意义)

运算的实质是以地址量的运算

1.指针的关系运算

+ - ++ -- (+= -=)
    p+q//没有意义
     p-q//表示指针之间相隔元素的个数
    
    p+1    
    p++    //表示地址值向大的方向偏移一个单位(数据类型)
    ++p    
    
    p--
    --p//表示地址值向小的方向偏移一个单位
    p-1
    
实际地址量:
p+n=    p保存的实际地址+sizeof(p的数据类型)*n(偏移量)

p-n同理

3.指针的赋值运算

1)将一个变量的地址赋值给一个具有相同类型的指针变量
    eg: int var =10;
        int *p;
        p=&var;
    指针的强转需要注意精度问题
    int类型的转换成char类型的,只会保存低8位(char是一个字节)的值

2)将一个指针变量赋值给一个具有相同类型的指针变量
    eg: int *p=&var;
        int *q;
        q=p;//p和q 都保存var的地址
        
3)将一个数组的首地址赋值给一个具有相同类型的指针变量
    eg: int arr[5];
        int *p=arr;

首地址+下标表示 数组
eg: 
    arr[i]<==>p[i] <==>*(arr+i)<==>*(p+i)
段错误:使用非法内存,看指针的++,--

    ++的优先级高于&和*
    a[]={5,8};
    p=&a[1];
    y=(*--p)++
    //y=5    (*--p)=a[0]=5
    //a[0]=6    y=a[0]++;,a[0]要加1

2.指针与二维数组

一维数组不能直接赋多维数组的首地址,需要行指针来赋值(p++无法判断是arr[1]还是arr[0] [1])

arr[i] [j]= * (*(arr+i)+j)

二维数组由多个一维数组组成,每个一维数组都是二维数组的元素,元素存取的是地址

arr[i]=*(arr+i)

数组名+1 移动一汉元素,二维数组名常被成为行地址

arr+1---> arr[1]

arr 第整个数组的首地址

*(arr) 第一个一维数组的首地址

1.行指针(数组指针)

用来存储行地址的指针变量成为行指针;

一般形式:

        <存储类型><数据类型> (*<指针变量名>)[表达式]

        表达式: 用来表示一行有几个元素,(列数)

        行指针变量 +1,移动一行元素;

eg: int (*p)[3]=arr; //类型为:int *[3] 把变量名删除,剩下的就是类型

不能用二级指针指向二维数组,指针的类型不同 ** 与 *[列数]

*( *(p+i)+j)<==> *( *(arr+i)+j)

arr:表示整个数组的首地址

arr[0]:表示第一行的首地址

arr+i: &arr[i]

*(arr+i): arr[i] arr[i]中存储着第i行的首地址也就是&arr[i] [0]

*(arr+i)+j: &arr[i] [j]

(*(arr+i)+j): arr[i] [j]

2字符指针

在程序中,初始化字符指针是把字符串的首地址赋给字符串

把一个字符指针直接初始化为一个字符串常量时,相当于让字符指针指向这个字符串的首地址,但是指针变量不能修改其目标的值;

eg: char *p="hello"; //p指向h

这样赋值之后不能修改*p的值

eg: *p='A';//error

可以改变p的指向

p=&c;

想字符指针赋给一个字符串常量时,指针应该指向一个储存空间

3.指针数组

指针数组:由若干个拥有相同的存储类型,数据类型的指针的集合(用来存储指针的数组)

一般形式:

<存储类型><数据类型> *<指针变量数组名>[表达式]

表达式:表示数组的大小,最多能存放指针变量的个数

数据类型:根据要存储的指针类型决定

eg: int arr[3] [2]={1,2,3,4,5,6};

int *p[3]={arr[0],arr[1],arr[2]};

printf("%d\n", * (*(p+i)+j));

注意&*a,a为值,对值取 * 不对

不能对野指针赋值

char *s; s="girls";√

二级指针与指针数组

char *parr[3]={"hello","world",how};

char **p=parr;

int i;

for(i=0;i<3;i++)

{

puts[parr[i]];

puts(*(p+i);

}

printf("%c\n",* (*(parr+i)+j) );

3.const

1.const 修饰常量吗?    不
2.const 修饰的变量一定不可以改变吗?
    const 修饰的局部变量可以通过指针修改目标的值
    
3.const常量化指针
    1)const int *p;    *p不可更改,p可以更改
      int const *p;(const在*P前)常量
      
    2)int * const p; *p可更改,p不可更改
                    (给p赋值只能初始化)
    
    3)const int * const p;*p不可更改,p也不可更改
                    (给p赋值只能初始化)

4.void

void型的指针变量是一种不确定数据类型得到指针变量,他可以通过强制转换让该变量指向任何数据类型的变量或数组。

void 指针需要强制转换后在做运算;

一般形式为:

void *<指针变量名称>;

5.函数

库函数:printf()、scanf()、gets()、getchar()

函数的定义:为了实现某一特定功能的独立模块;

5.1函数的一般形式

<存储类型><数据类型> <函数名>(形式参数说明)
{
    函数体;
    return 表达式;
}
存储类型默认为extern,方便其他文件调用;🥶🥶🥶🥶🥶
数据类型:与返回值保持相同;如果数据类型为void,不需要返回值,return;

return:结束当前模块,作为函数返回值;
形式参数说明(形参):为了告诉函数调用者,在调用函数时,传递的参数的个数及其具体类型;如果形参有多个,以,隔开。形参可以没有,函数不需要传参时

函数体:可以为大于等于零条语句;

5.2函数的调用

函数名(实际参数);

eg:

scanf("%d",&var);

gets(buf);//s指向了buf (char *gets(char * s))

......

实际参数(实参):给形式参数赋值,没有形参就没有实参

5.4函数的说明

函数的原型:

<数据类型> <函数名>(形式参数说明);

注意:函数在说明是,形式参数可以只给类型

5.5函数的传参

把实参赋值给形参

形参=实参;函数中处理的是形参 

函数内部不要有打印

函数内部的变量是局部变量,如要返回局部变量,需要用static来延长生命周期。

5.5.1值传递(复制传递)

把实参的值赋值给形参

eg:从终端输入两个数x,n,请封装一个函数,求x的n次方

从终端输入两个数,请封装函数,实现两个数的交换

5.5.2地址传递

把实参的地址传递给形参,(形参指向实参)形参可以修改实参的值

通过地址来访问数据,修改数据,而不是修改地址;

可以重用值传递来返回函数的运算结果,

void add(int x,int y,int* sum);

add(10,20,&sum);

请输入一串数据,请封装一个函数,求和

5.5.3全局变量传递

5.6数组作为参数传参

数组的传参需要传递首地址和数组长度(长度在功能函数中无法求取);

复制传递:实际上形参也是指针变量,只是形式不同,实际用法和地址传递一样
    eg:
        int addFunc(int parr[100])
        parr依旧是一个指针,而不是数组名,100没有用,可以不写
        sizeof(parr)=8
        
地址传递:
    eg:
        int addFunction(int *parr)

5.7指针函数

返回值为一个地址的函数:

一般形式:

<数据类型> * <函数名>(形式参数说明)

{ ​ 函数体; ​ return 表达式; ​ }

eg:封装一个函数来求二维数组的和

二维数组的传参分两种:

普通指针:&arr[m] [n] arr[m] 用*p来接收 *(p+i),i<m * n

数组是线性的

行指针: arr 用(*p)[n]来接收 *( *(p+i)+j);

行指针有限制,数组的定义,列数需要与行指针的列数表达式值一致,arr[m] [n] (*p)[n]

5.8函数指针

指向一个函数,保存函数的地址(入口、节点)
函数指针是用来存放函数的地址,这个地址是一个函数的入口地址,而且是函数调用时使用的的起始地址;
函数指针可以减函数作为参数传递给其他参数调用
一般形式:
    <数据类型>(*函数指针名)(形参)
    数据类型和形参需要根据指针的目标函数确定
    eg:
        int add(int,int);
        int (*p)(int,int);
        p=add;
        
        p(2,3);

5.9函数指针数组

就是可以保存多个函数名(函数指针)的数组
一般形式:
    <数据类型>(*函数指针名[大小])(形参)
    
    eg:
    int (*parr[2])(int,int)
    parr[0]=add;
    parr[0](100,20)

5.10回调函数

把一个函数作为形参来传递,再使用这个函数,回调函数声明调用函数时,只声明函数类型,不声明具体的变量

eg:

int test(int x,int y,int (*pfunc)(int,int))

{

return pfunc(x,y);

}

test(25,35,add);(add是自己写的求和的函数)

 return pfunc(x,y);        才对,也错了一个能运行,说明有一定的纠错机制,但还是应该正确调用

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值