黑马程序员——C语言知识点总结之指针和关键字static、extern

时间:2023-02-17 10:02:57

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流!——

指针

简介

指针的基本概念 在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此, 一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。 定义指针的目的是为了通过指针去访问内存单元。

指针变量的定义

定义格式

指针变量的定义格式:
类名标识符 *指针变量名;(int *p;)
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类名标识符表示本指针变量所指向的变量的数据类型。

例如: int *p;表示p是一个指针变量,它的值是某个整型变量的地址。 或者说p指向一个整型变量。
staic int *p1; /*p1是指向静态整型变量的指针变量*/
float *p2; /*p2是指向浮点变量的指针变量*/
char *p3; /*p3是指向字符变量的指针变量*/
应该注意的是,一个指针变量只能指向同类型的变量,如p2 只能指向浮点变量,不能时而指向一个浮点变量, 时而又指向一个字符变量。

指针变量的赋值

指针变量的赋值只能赋予地址, 决不能赋予任何其它数据。在C语言中, 变量的地址是由编译系统分配的,C语言中提供了地址运算符&来表示变量的地址。

其一般形式为: & 变量名;
如&a变示变量a的地址,&b表示变量b的地址。

1、先定义后赋值
简单取值:
int a = 10;
int *p;
p = &a;
printf(“%d”, *p);
简单改值:
*p = 9;

2、定义的同时赋值
int a = 10;
int *p = &a;

注意:
1)%p输出指针里面存储的地址值
2)不能乱用类型,比如int a = 10; float *p = &a;
3)不允许把一个数赋予指针变量,故下面的赋值是错误的:
int *p;
p=1000;
4)被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的
5)清空指针
p = 0;
p = NULL;

指针变量的运算

指针变量可以进行某些运算,但其运算的种类是有限的。 它只能进行赋值运算和部分算术运算及关系运算。

指针运算符

1、取地址运算符&
取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量的地址。

2、取内容运算符*
   取内容运算符*是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。

int main()
{
int a=5,*p=&a;
printf ("%d",*p);
return 0;
}

表示指针变量p取得了整型变量a的地址。本语句表示输出变量a的值。

指针变量的运算

指针变量的赋值运算有以下几种形式:

1)指针变量初始化赋值,前面已作介绍。

2)把一个变量的地址赋予指向相同数据类型的指针变量。

例如:
int a,*p;
p=&a;
把整型变量a的地址赋予整型指针变量p

3)把一个指针变量的值赋予指向相同类型变量的另一个指针变量。

如:

int main()
{
int a = 10;
int *pa = &a;
int *pb;
pb=pa;
printf("%d\n",*pb);
return 0;
}

运算结果:10
由于pa,pb均为指向整型变量的指针变量,因此可以相互赋值。

4)指针变量指向数组

例如:
int ages[5],*p;
p=ages; (数组名表示数组的首地址,故可赋予指向数组的指针变量pa)
也可写为:
p=&ages[0]; (数组第一个元素的地址也是整个数组的首地址)
相应的:
p —> &ages[0]
p + 1 —> &ages[1]
p + 2 —> &ages[2]
p + i —> &ages[i]

int main()
{
int *p;
int ages[5] = {10,9,8,7,6};
p = &ages;
printf("p = %d\n",*p);
printf("p+1 = %d\n",*(p+1));
printf("p+2 = %d\n",p[2]);
return 0;
}

运行结果:
p = 10
p+1 = 9
p+2 = 8

5)指针变量指向字符串

利用指针定义字符串
char *name = “itcast”;
char *names[5] = {“jack”, “rose”, “jake”};

特点:字符串其实是一个常量字符串,里面的字符是不能修改
使用场合:字符串的内容不需要修改,而且这个字符串经常使用

6)把函数的入口地址赋予指向函数的指针变量

定义指向函数的指针:
double (p)(double, char , int);
p = haha;
或者
double (p)(double, char , int) = haha;

间接调用函数:
1> p(10.7, “jack”, 10);
2> (*p)(10.7, “jack”, 10);

#include <stdio.h>

int sum(int a, int b)
{
return a + b;
}

int main()
{
// 定义指针变量指向sum函数
// 左边的int:指针变量p指向的函数返回int类型的数据
// 右边的(int, int):指针变量p指向的函数有2int类型的形参
int (*p)(int, int);
p = sum;
int c = p(10, 11);
int d = (*p)(10, 11);
printf("c=%d,d=%d\n", c,d);
return 0;
}

运行结果:
c=21,d=21

本章小结

1、指针是C语言中一个重要的组成部分,使用指针编程有以下优点:

(1)提高程序的编译效率和执行速度。

(2)通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯。

(3)可以实现动态的存储分配。

(4)便于表示各种数据结构,编写高质量的程序。

2、指针的运算

(1)取地址运算符&:求变量的地址

(2)取内容运算符*:表示指针所指的变量

(3)赋值运算
·把变量地址赋予指针变量
·同类型指针变量相互赋值
·把数组,字符串的首地址赋予指针变量
·把函数入口地址赋予指针变量

(4)加减运算
对指向数组,字符串的指针变量可以进行加减运算,如p+n,p-n,p++,p–等。对指向同一数组的两个指针变量可以相减。对指向其它类型的指针变量作加减运算是无意义的。

(5)关系运算
指向同一数组的两个指针变量之间可以进行大于、小于、 等于比较运算。指针可与0比较,p==0表示p为空指针。

3、与指针有关的各种说明和意义见下表

int *p;     p为指向整型量的指针变量
int *p[n];   p为指针数组,由n个指向整型量的指针元素组成。
int (*p)[n];  p为指向整型二维数组的指针变量,二维数组的列数为n
int *p()    p为返回指针值的函数,该指针指向整型量
int (*p)()   p为指向函数的指针,该函数返回整型量
int **p     p为一个指向另一指针的指针变量,该指针指向一个整型量。

4、有关指针的说明很多是由指针,数组,函数说明组合而成的。
但并不是可以任意组合,例如数组不能由函数组成,即数组元素不能是一个函数;函数也不能返回一个数组或返回另一个函数。例如
int a5;就是错误的。

关键字static和extern

外部函数:如果在当前文件中定义的函数允许其他文件访问、调用,就称为外部函数。C语言规定,不允许有同名的外部函数。

内部函数:如果在当前文件中定义的函数不允许其他文件访问、调用,只能在内部使用,就称为内部函数。C语言规定不同的源文件可以有同名的内部函数,并且互不干扰。

extern与函数

对函数的作用

1、extern对函数的作用:
1> 完整地定义一个外部函数
2> 完整地声明一个外部函数
(extern可以省略,默认情况下声明和定义的函数都是外部函数)

2、C语言规定不允许有同名的外部函数,链接的时候链接器会发现在不同源文件中定义了同一个函数,会直接报错。

// 完整地声明一个外部函数
extern void test();
// extern可以省略
// void test();

void test()
{
printf("这是一个test函数\n");
}

int main()
{
test();

return 0;
}

对变量的作用

1、声明一个已经定义过的外部变量,在函数前声明,变量的定义的位置不限

2、extern可以用来声明一个外部变量,但是不能用来定义变量;默认情况下,一个外部变量是可以供多个源文件共享的,也就说,多个源文件中同名的外部变量都代表着同一个变量

#include <stdio.h>

void test();

// 定义一个外部变量,不能用extern定义

//int a; 这么多个a都是代表同一个a
//int a;
//int a;
//int a;
//int a;

// 声明一个外部变量
extern int a;

int main()
{
a = 10;

printf("a的值是%d\n", a);*/

return 0;
}

static与函数

对函数的作用

1、定义内部函数
在定义函数的时候加个static关键字,就是定义一个”内部函数”,也就是其他文件不能访问本文件中定义的函数。

2、声明内部函数
我们还可以用static声明一个内部函数

例:

#include <stdio.h>

// 声明一个内部函数
static void test2();

// 定义一个内部函数
static void test2()
{
printf("调用了test2函数\n");
}

注:内部函数static不可省

对变量的作用

1、定义一个内部变量,不同文件的同名内部变量互不影响

2、static修饰局部变量:
1> 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
2> 并没有改变局部变量的作用域
3> 所有的test函数都共享着一个变量b

3、static修饰局部变量的使用场合:
1>如果某个函数的调用频率特别高
2>这个函数内部的某个变量值是固定不变的

#include <stdio.h>

void test()
{
int a = 0;
a++;
printf("a的值是%d\n", a); // 1

// static修饰局部变量,延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁,所有的test函数都共享着一个变量b
static int b = 0;
b++;
printf("b的值是%d\n", b); // 3
}

int main()
{
// 每执行一次循环,a重新初始化为0,b未被销毁
for (int i = 0; i<5; i++)
{
test();
}

test(); // a=1,b=6

test(); // a=1,b=7

test(); // a=1,b=8

return 0;
}

运行结果:
a的值是1
b的值是1
a的值是1
b的值是2
a的值是1
b的值是3
a的值是1
b的值是4
a的值是1
b的值是5
a的值是1
b的值是6
a的值是1
b的值是7
a的值是1
b的值是8

编译与链接

所谓编译,就是单独检查每个源文件的语法是否合理,并不会检查每个源文件之间的关联关系,一个源文件编译成功就生成一个目标文件。

所谓链接,就是检查目标文件的关联关系,将相关联的目标文件组合在一起,生成可执行文件。