1.指针的基本概念和使用
1.1.指针的基本概念:
计算机硬件系统的内部存储器中,拥有大量的存储单元(容量为1字节)。为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个唯一的地址。内存单元的编号也叫做地址,既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。内存单元的指针和内存单元的内容是两个不同的概念。
总之,对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该内存单元的内容。
1.2.使用指针的好处
1)为函数提供修改调用变量的灵活手段。
2)让函数有多个返回值。
3)可以改善某些子程序的效率。
4)为动态数据结构(如二叉树、链表)提供支持。
1.3.变量的存取方式:
1)直接存取:变量的赋值和取值(通过变量名)。
2)间接存取:通过指针(地址)间接操作完成。
1.4.在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
注意:严格意义上说,指针是一个地址,是一个常量。指针变量存放一个地址,是一个变量。
1.5.定义一个指针变量:
一般形式: 类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
注意:
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3)指针也可以被声明为全局、静态局部和局部的。
1.6.指针变量的初始化
指针变量的初始化方法有两种:定义的同时进行初始化和先定义后初始化
注意点:
1)多个指针变量可以指向同一个地址。
2)指针的指向可以改变。
3)指针没有初始化里面是一个垃圾值,这时候我们称这是一个野指针,如果操作一个野指针,可能会导致程序崩溃,也可能会访问不该访问的数据。所以指针必须初始化才可以访问其所指向的存储区域。
1.7.指针变量的使用: *指针变量名 // 获取指针变量指向的存储空间的内容
使用举例:
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 定义一个变量
int a = 100;
// 定义一个指针变量
int *p;
// 用定义的变量初始化指针变量
p = &a;
// 使用指针变量
int value = *p;
// 输出
printf("value = %d", value);
return 0;
}
2.指针的应用场景:
1)在被调函数中修改主调函数变量的值
2)让函数有多个返回值
实例1:用函数实现两个变量值的交换。
#include <stdio.h>
// 定义函数,实现两变量值互换
void swap(int *p1, int *p2){ // 传入两个指针
// 两指针指向存储空间的内容互换
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main(int argc, const char * argv[]) {
// 定义两个变量
int a = 2;
int b = 3;
// 打印互换前的值
printf("互换前:a = %d, b = %d\n", a, b);
// 定义两个指针,并指向两个变量
int *p1 = &a;
int *p2 = &b;
// 调用函数实现两个变量值的交换
swap(p1, p2);
// 打印互换后的值
printf("互换后:a = %d, b = %d\n", a, b);
return 0;
}
实例2:用一个函数进行加减乘除运算。
#include <stdio.h>
/**
* 对传入的两个参数进行四则运算,并将计算结果赋值给传入的相应地址
*
* @param a 用于计算的第一个数
* @param b 用于计算的第二个数
* @param sum 和
* @param minus 差
* @param multiply 积
* @param devide 商
*/
void calculate(double a, double b , double *sum, double *minus, double *multiply, double *divide){
// 在函数内部访问了主调函数中的变量值
*sum = a + b;
*minus = a - b;
*multiply = a * b;
*divide = a / b;
}
int main(){
// 定义变量
double a = 23;
double b = 11;
double sum = 0, minus = 0, muntiply = 0, divide = 0;
// 调用函数进行运算
calculate(a, b, &sum, &minus, &muntiply, ÷);
// 输出计算结果
printf("sum = %.2f\nminus = %.2f\nmultiply = %.2f\ndivide = %.2f\n", sum, minus, muntiply, divide);
return 0;
}
3.数组指针的基本概念和使用
数组指针:指向数组元素的指针。
数组指针的作用:使用数组指针间接访问数组的元素
数组指针的定义:数组元素的变量类型 *指针变量名;
数组指针的初始化:
int a[4] = {1,2,3,4};
int *p = a; // 数组指针,定义了一个指针变量p指向数组的第一个元素。等价于 int *p = &a[0];
数组指针如何访问数组的元素:
1)p + 1 表示指向数组的下一个元素
2)p - 1 指向数组的上一个元素
引用一个数组元素的方法(a是数组名,p是指针变量):
1)下标法:如 a[i]
2)指针法:如*(a + 1)或*(p + i)
3)a是常量,不能a ++, p是变量,可以用p++
实例:有一个整型数组a,有10个元素,要求用不同方法输出数组中的全部元素。
#include <stdio.h>
/**
* 下标法输出数组的10个元素
*
* @param a 传入的数组
*/
void print1(int a[]){
printf("用下标法输出数组元素:");
for (int i = 0; i < 10; i ++) {
printf("%d,", a[i]);
}
printf("\n");
}
/**
* 指针法输出数组的10个元素
*
* @param a 数组名
*/
void print2(int *a){
printf("用指针法输出数组元素:");
for (int i = 0; i < 10; i ++) {
printf("%d,", *(a + i));
}
}
int main(int argc, const char * argv[]) {
// 定义并初始化一个数组
int a[10] = {1,2,3,4,5,6,7,8,9,0};
// 用下标法输出数组元素
print1(a);
// 用指针法输出数组元素
print2(a);
return 0;
}
2.实现:用指针将数组a中n个整数按相反顺序存放,然后输出
#include <stdio.h>
/**
* 逆序一个数组
*
* @param a 数组名
* @param len 数组长度
*/
void niXu(int a[], int len){
// 定义下标
int i = 0;
int j = len - 1;
// 定义中间变量
int temp;
while (i < j ) { // 当下标i < j 时
// 交换a[i]和a[j]
temp = *(a + i);
*(a +i) = *(a + j);
*(a + j) = temp;
// 修改下标
i ++ ;
j --;
}
}
int main(){
// 定义并初始化一个数组
int a[10] = {1,2,3,4,5,6,7,8,9,10};
// 将数组按相反顺序存放
niXu(a,10);
// 输出
for (int i = 0; i < 10; i ++) {
printf("%d,",a[i]);
}
return 0;
}
4.指针数组的基本概念
一维指针数组:
数组的元素值为指针的数组是指针数组。指针数组是一组有序的指针的集合。指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。
指针数组的一般形式为: 类型说明符 *数组名[数组长度]
其中类型说明符为指针值所指向的变量的类型。
例如: int *pa[3]; // 表示pa是一个指针数组,它有三个数组元素,每个元素值都是一个指针,指向整型变量。
指针数组的使用:
int a = 3, b = 4, c = 5;
int *pa[3] = {&a,&b,&c};
pa[0] // a 的地址
pa // 数组的首地址,pa[0]的地址
两个指针变量之间的运算
1)两个指针之间的减法运算:两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址)相减之差再除以该组元素的长度(字节数)。
常见用法:
1)判断两个指针变量指向的元素是否连续
2)判断两个指针变量之间相隔几个元素
2)两个指针之间的关系运算:
如果 p1>p 为真,表示p1在高位,如果为假,则表示p在高位或他们指向了同一个位置。
5.二维数组指针
数组指针:定义一个指针变量,让这个指针变量指向一维数组的元素。
二维数组指针:行指针,用来指向二维数组的每一行,存放的是行的首地址。
定义格式: 数据类型 (*行指针变量名)[数组第二维的长度];
二维数组指针的初始化
int a[2][3];
// 假设我要定义一个指向数组a的一个行指针
// int (*p)[3] = a;
二维数组指针的使用
*(*(p + i) + j) // 获取二维数组的元素
指针数组和二维数组指针的区别
int *pa[3] = {&a,&b,&c}; // pa是一个指针数组
int *pa1[2] = {a[0],a[1]};
int (*pa)[3]; // 二维数组指针
指针数组和二维数组指针虽然都可以用来表示二维数组,但是其表示方法和意义是不同的
如: int (*p)[3]; // 表示一个指向二维数组的指针变量。该二维数组的列数为3或分解为一维数组的长度为3。
int *p[3]; // 表示p是一个指针数组,有三个下标变量p[0],p[1],p[2]均为指针变量。
例:二维数组指针的使用。
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 定义并初始化一个二维数组
int a [3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
// 定义并初始化一个二维数组指针
int (*p)[4] = a;
// 遍历并输出二维数组元素
for (int i = 0; i < 3; i ++) {
for (int j = 0; j <4; j ++) {
// printf("%d\t", *(*(a + i) + j));
printf("%d\t", *(*(p + i) + j)); // 二维数组指针可以代替数组名使用
}
printf("\n");
}
return 0;
}
字符串指针:
定义: char *字符串指针变量名 = “字符串内容”;
作用:用来保存一个字符串。
如:
char *str = "I want join itheima!"
// "I want join itheima!"这个字符串保存在内存的常量区,常量区是只读的,所以不可以修改字符串中的字符
// str只保存了字符串常量的地址
应用:输入5个国名并按字母顺序排列后输出
#include <stdio.h>
#include <string.h>
/**
* 按字母顺序排列字符串数组中的字符串
*
* @param str 传入的字符串数组
*/
void sortWihtAlphabet(char *str[5]){
char *temp; // 定义中间变量
// 冒泡排序
for (int i = 0; i < 4; i ++) {
for (int j = 0; j < 4 - i; j ++) {
if (strcmp(str[j], str[j+1]) > 0) { // 如果前一字符串比后一字符串大
// 则交换位置
temp = str[j];
str[j] = str[j+1];
str[j+1] = temp;
}
}
}
}
int main(int argc, const char * argv[]) {
// 定义初始化一个字符串指针数组
char *str[5] = {"CHINA","AMERICA","AUSTRALIA","FRANCE","GERMAN"};
// 按字母顺序排列
sortWihtAlphabet(str);
// 输出排列后的字符串数组
for (int i = 0; i < 5; i ++) {
printf("%s\n", str[i]);
}
return 0;
}