C学习笔记(3):指针

C学习笔记(3):指针

C C学习笔记 指针


指针就是地址,所有出现指针的地方都可以将其换成地址

指针变量就是地址变量,是用来专门储存地址的变量,所以取用指针的值是没有意义的,加一个\号来取用指针变量储存的地址内的值*

指针的定义和赋值

1
2
3
4
5
6
7
8
#include<stdio.h>
void main(){
int a = 3;
int *p;
p = &a;
printf("%d",*p);//输出:3
}


一、通过指针引用数组

数组名:

  • 一维数组:数组名即数组首元素的地址,如int a[10], a即第0个元素的地址

  • 二维数组:数组名加行数表示每一行行首元素,如int a[2][3],a[1]即数组第二行元素的首元素a[1][0]地址

1.一维数组

C语言的函数调用的原理是“值传递”,所以当需要用函数处理数组时,函数的形参实际上是指针变量,然后将数组的首元素地址传入形参。例:

#define size 10;
void main(){
    int a[size];
    fun(a,size);
}
void fun(int *p,int n){
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//示例:打印一个一维数组
#define size 5
#include<stdio.h>
void fun(int *p,int n){
for(int i=0;i<size;i++,p++){
printf("%d\t",*p);
}
}
void main(){
int a[size]={1,2,3,4,5};
fun(a,size);//输出结果:1 2 3 4 5
}


2.二维数组

C语言中,二维数组是数组的数组,即他是一个元素为数组的数组

一般形式:

int array[raw][col] = {{},{},{}..}

用法:

int a[3][4];
int (*p)[4];//定义一个指向含有四个元素的一维数组的指针
p = a;
  1. a[1]:二维数组第二行行首元素的地址,不存在这个元素,只能返回地址
  2. *(p+1):指向第二行行首元素,等于a[1],由于不存在这个元素,所以他的值就是a的第二行的首元素的地址
  3. (\(p+1)+1):指向第二行第二列元素,等同于a[1][1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//示例:测试以上三个变量
#define raw 3
#define col 4
#include<stdio.h>
void main(){
int a[raw][col];
int (*p)[col];
p = a;
int n=1;
for(int i=0;i<raw;i++){
for(int j=0;j<col;j++){
a[i][j] = n++;
}
}
printf("%d\n",*(p+1));//输出:例:1606416032
printf("%d\n",a[1]);//输出:例:1606416032
printf("%d\n",*(*(p+1)+1));//输出:6
}


二、通过指针引用字符串

C语言中没有字符串变量,只有字符串数组,字符串的储存是通过储存着字符变量的数组来实现的。在字符串数组的最后一位是系统自动添加和默认的结尾符号\0,当编译器见到\0,即停止字符串的读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//示例:利用指针进行字符串复制
#include<stdio.h>
void copy_str(char *from,char *to){
while (*b != '\0') {
*from++ = *to++;
}
*from++ = '\0';//在得到的字符串数组后加一个系统识别的结束符号
}
void main(){
char a[] = "I am a girl";
char b[] = "Hello world";
copy_str(a,b);
printf("%s\n",a);
}


三、通过指针引用函数

例:

int sum (int,int);
int (*p)(int,int);

p指针可以指向参数列表为(int,int)的函数

难点1:用指向函数的指针作函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//示例:
#include<stdio.h>
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a<b?a:b;
}
int sum(int a,int b){
return a+b;
}
void fun(int a,int b,int (*p)(int,int)){
printf("result:%d\n",(*p)(a,b));
}
int main(){
int a=3;
int b=5;
int c;
printf("Please input 1,2or3:\n");
scanf("%d",&c);
switch (c) {
case 1:
fun(a, b, max);
break;
case 2:
fun(a, b, min);
break;
case 3:
fun(a, b, sum);
break;
default:
printf("You have insert wrong thing!\n");
break;
}
return 0;
}

用该方式可以使得函数的地址可以在不同的函数中传递,提高了程序的有序性

难点2:返回指针值的函数

一般形式:类型名 *参数名(参数列表)

该函数的返回值是指向整形的指针,即一个地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//示例:通过返回指针值的函数的方式输出数组
#include<stdio.h>
int *p(int *arr,int n){
return arr+n;
}
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,10};
int *b;
int c;
printf("Please input the arr_num:\n");
scanf("%d",&c);//输入:2
b = p(a,c);
printf("%d\n",*b);//输出:3,即a[2]
return 0;
}


四、指针数组

指针数组:数组的元素为指针

一般形式:类型名 * 数组名[数组长度]

指针数组的一个作用是main()函数的参数列表

main函数一般是有参数的,main函数的定义形式一般为:

int main(int argc,char * argv[]){
    return 0;
}

括号中的参数一般由操作系统传入,即实参和运行文件的命令一起给出,命令名 参数1 参数2 ... ,例:

file1 China Beijing

就将两个单词传入main函数了。

  1. argc:此时argc的值为3,命令行有三个单词
  2. *argv[]:这是一个指向char的指针数组,说明命令行输入的都被读取为字符串类型,里面的每一个元素都是一个命令的首字母的地址

五、动态内存分配

1.malloc函数

一般形式:void * malloc(unsigned int size)

该函数的返回值即一个开辟空间的首位的地址,其类型是void,即只是一个地址。若执行失败(如内存不足),则返回空指针(NULL)

例:malloc(100)就开辟了一段100字节的空间

2.calloc

一般形式:void * calloc(unsigned n,unsigned size)

开辟n个长度为size的连续空间

用calloc函数可以为一维数组开辟动态储存空间,n为数组元素个数,每个元素的长度size

例:p = calloc(50,4)


3.free

一般形式:void free(void p)

释放指针变量p所指向的动态空间,这个p应该是最近一次调用calloc或者malloc函数时得到的函数返回值


4.realloc

一般形式:void realloc(void p, unsigned int size)

重新分配p所指向的动态空间的大小,size为重新分配的大小,若重新分配不成功,返回NULL

例:p = realloc(p,50)