一、指针概述
1.1 指针相关概念
1> 引入目的:能够从地址的角度,找到内存中的数据,而不是以变量的角度去找,效率较高
2> 指针:就是内存地址编号
3> 指针变量:由于指针这个地址编号很难记忆,我们引入指针变量存储指针
存储地址的变量称为指针变量
4> 指针变量中,指针存储地址,作为一个特殊的数据类型,其大小是固定的 8 字节
1.2 指针变量的定义
1> 定义格式:数据类型 * 指针名;
例如:int * ptr;
2> 指针变量的初始化
1、使用一个相同数据类型的变量的地址为其进行初始化 int num = 520; int * ptr = # //将num的地址赋值个指针变量 ptr //定义一个指针变量ptr,指向num 2、使用一个已经初始化了的指针变量给一个新的指针变量进行初始化 int * qtr = ptr; //此时表示两个指针变量同时存储了num的地址 int *qtr = # 3、使用地址的0 就是 NULL为其初始化 int *wtr = NULL; 4、注意:没有初始化的指针变量不能直接使用,因为该指针变量中存储了一个随机地址,如果对其进行更改,可能会导致系统瘫痪 5、野指针:指向非法内存的指针称为野指针 产生情况: 1)定义指针时,没有为其进行初始化 2) 指向了一个内存空间,但是随着程序的进行,该内存空间被回收了,那么该指针也是野指针(悬空指针) 3) 数组下标越界时,访问的地址也是野指针
3> 指针变量的使用:使用运算符 ‘ * ’来取得指针变量中的值,我们也称该运算为 取值运算
1、指针变量在定义时,*号只是身份的象征,表示该语句定义的是指针变量 2、指针变量使用是,*号表示一个运算,取值运算,就是取得该指针变量指向的内存空间中的值 3、总结一下 * 号在C语言中的用途 1) 表示乘号,是一个双目运算符 2) 定义指针时,是身份的象征,表示定义的是指针变量 3) 使用指针时,是取值运算符,表示取得指针变量指向的内存空间中的值 4、总结一下 & 在C语言中的用途 1) 一个&表示双目运算符 按位 与运算 2) 两个&&表示双目运算符 逻辑 与运算 3) 一个&表示单目运算符 取址运算 5、&与*在指针的方面,互为逆运算 int num = 520; int *ptr = # 1) *&num ==> *(&num) ==> *(ptr) ==> num 2) &*ptr ==> &(*ptr) ==> &(num) ==> ptr 3) *&ptr ==>*(&ptr) ==> *(ptr的二级地址) ==> ptr 4) &*num ==>报错
#include<myhead.h> int main(int argc, const char *argv[]) { int num = 520; printf("num = %d\n", num); //num的值 printf("&num = %p\n", &num); //输出变量的地址 //定义指针变量指向num int *ptr = # //该语句执行后:num <==> *ptr printf("ptr = %p\n", ptr); //得到的是num的地址 //通过ptr获取num的值 printf("*ptr = %d\n", *ptr); //输出的是num的值 //解释解释什么叫等价 num = 1314; printf("*ptr = %d\n", *ptr); //输出的是num的值 *ptr = 999; printf("num = %d\n", num); //num的值 return 0; }
4> 指针的大小与指针的类型之间的关系
不同数据类型的指针所占内存空间都是一样的,都是指针的大小,32位系统下为4字节,64位系统下为 8字节
指针的数据类型存在的意义,不是为了给指针分配内存空间的,而是为了指针偏移使用的
不同类型的指针,在进行指针偏移时,偏移的大小跟指针的数据类型有关
指针每偏移一个单位,内存空间就会偏移一个数据类型大小的字节数
指针指向普通变量时,指针的偏移是没有意义的,但是,指针指向数组时,指针的偏移就有了现实的意义,表示指向上一个元素或者下一个元素的地址
#include<myhead.h> int main(int argc, const char *argv[]) { char value_1 = 'H'; //定义字符数据 short value_2 = 520; //定义短整形数据 int value_3 = 1314; //定义整形数据 double value_4 = 3.14; //定义双精度浮点型数据 //定义指针指向普通变量 char *ptr1 = &value_1; short *ptr2 = &value_2; int * ptr3 = &value_3; double *ptr4 = &value_4; printf("ptr1 = %p\n", ptr1); printf("ptr2 = %p\n", ptr2); printf("ptr3 = %p\n", ptr3); printf("ptr4 = %p\n", ptr4); printf("*************************************\n"); printf("ptr1+1 = %p\n", ptr1+1); printf("ptr2+1 = %p\n", ptr2+1); printf("ptr3+1 = %p\n", ptr3+1); printf("ptr4+1 = %p\n", ptr4+1); return 0; }
5> 指针可以使用的运算
关系运算: == 判断两个指针是否指向同一个内存地址,ptr==NULL,判空指针
数加运算:表示对指针进行偏移
取值运算(*)、取址运算(&)
二、指针指向普通变量作为函数参数
1> 指针作为函数的参数进行数据传递时,不一定是地址传递
、
作业
1> 使用递归实现 求 n 的 k 次方
2> 使用递归实现 strlen 的功能
3> 使用递归实现汉诺塔问题(君子作业)
4> 定义一个函数将一个字符串从大到小排序
5> 实现一个函数,用于检查一个字符串是否是回文字符串(正序和反序都相同)
6> 使用指针完成判断自己的主机存储多字节整数时,是大端存储还是小端存储
7
1.
#include <stdio.h>
long power(int n, int k) {
if (k == 0) {
// 任何非零数字的0次方都是1
return 1;
} else if (k > 0) {
// 递归调用,k减1直到k为0
return n * power(n, k - 1);
} else {
// 如果k为负数,则计算1/n的-k次方
return 1 / power(n, -k);
}
}
int main() {
int a, b;
printf("请输入数字: ");
scanf("%d", &a);
printf("请输入此向米: ");
scanf("%d", &b);
long result = power(a, b);
printf("%d %d %ld\n", a, b, result);
return 0;
}
2.
#include <stdio.h>
// 递归函数声明
size_t my_strlen(const char *str);
int main() {
const char *test_str = "Hello, World!";
size_t len;
// 调用递归函数
len = my_strlen(test_str);
printf("这个lenth为: %zu\n", len);
return 0;
}
// 递归函数定义
size_t my_strlen(const char *str) {
if (*str == '\0') { // 如果遇到字符串结束标志
return 0; // 返回0
} else {
// 递归调用,当前字符后面的所有字符的长度加1
return 1 + my_strlen(str + 1);
}
}
3.
#include <stdio.h>
void hanoi(int n, char from_rod, char to_rod, char aux_rod) {
if (n == 1) {
// 当只有一个盘子时,直接从起始杆移动到目标杆
printf("将第1个盘子从杆 %c 移动到杆 %c\n", from_rod, to_rod);
} else {
// 递归地将 n-1 个盘子从起始杆移动到辅助杆
hanoi(n - 1, from_rod, aux_rod, to_rod);
// 将剩下的一个盘子从起始杆移动到目标杆
printf("将第%d个盘子从杆 %c 移动到杆 %c\n", n, from_rod, to_rod);
// 递归地将 n-1 个盘子从辅助杆移动到目标杆
hanoi(n - 1, aux_rod, to_rod, from_rod);
}
}
int main() {
int n;
printf("请输入盘子的数量: ");
scanf("%d", &n);
// 调用汉诺塔函数,A 为起始杆,B 为辅助杆,C 为目标杆
hanoi(n, 'A', 'C', 'B');
return 0;
}
4
#include <stdio.h>
#include <string.h>
void sortStringDesc(char *str) {
int len = strlen(str);
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (str[j] < str[j + 1]) { // 如果当前字符小于下一个字符,则交换位置
char temp = str[j];
str[j] = str[j + 1];
str[j + 1] = temp;
}
}
}
}
int main() {
char str[] = "hello world";
printf("原来: %s\n", str);
sortStringDesc(str);
printf("从大到小: %s\n", str);
return 0;
}
6
#include <stdio.h>
union EndianCheck {
int asInt;
char asChar[4];
} check;
int main() {
check.asInt = 0x01020304; // 假设这是一个32位的int
if (check.asChar[0] == 0x01) {
printf("大.\n");
} else if (check.asChar[0] == 0x04) {
printf("小.\n");
} else {
printf("为\n");
}
return 0;
}
7
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
#define MAX_WORDS 100
// 函数声明
int countWords(const char *str);
int main() {
char line[MAX_LINE_LENGTH];
int wordCount;
// 读取一行输入
fgets(line, MAX_LINE_LENGTH, stdin);
// 统计单词数量
wordCount = countWords(line);
printf("单词数量为: %d\n", wordCount);
return 0;
}
// 函数定义
int countWords(const char *str) {
int wordCount = 0;
int inWord = 0;
while (*str) {
if (isalpha(*str)) {
// 如果当前字符是字母并且之前不在单词内,则开始一个新的单词
if (!inWord) {
inWord = 1;
wordCount++;
}
} else {
// 如果当前字符不是字母并且之前在单词内,则结束单词
inWord = 0;
}
str++;
}
return wordCount;
}