1 指针的定义和使用
指针的定义和使用:
1、指针:是一种数据类型 指针变量也是一种变量
2、指针格式: 对应的数据类型 * p:指针类型变量 用来指向一个变量的地址
3、通过指针修改变量的值
*p = 200;
4、指针类型在内存中的大小 : 在32位操作系统中所有指针大小都是4个字节大小
打印指针内存大小格式: sizeof(int *) || sizeof(p)
5、内存按照 unsignned int 为每个一个内存分配编号
6、讲解:
①定义变量 int a =10; 数值10 存在内存 为a开辟的空间中 a的地址为0xff00
②定义指针 int * p = &a 指针变量p 在内存中存贮的是 a的地址 0xff00,指针变量p 在内存中的大小在32位系统中都是4个字节大小
③*p = 200 通过操作指针变量p 所存储的 a的地址 来改变a的值
④指针p 有一个自己的内存地址 指针p地址 与 定义变量a的地址 0xff00不同
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int a = 10;
a = 20;
int b = 100;
//指针是一种数据类型 p是指针类型变量 用来指向一个变量的地址
int* p = &a;
p = &b;
printf("%p\n", &b);
printf("%X\n", p);
//通过指针修改变量的值
*p = 100;
printf("%d\n", b);
printf("%d\n", *p);
//sizeof()指针类型在内存中的大小 在32位操作系统中所有指针大小为4个字节大小
//在64位操作系统中所有指针大小为8个字节大小
printf("%d\n", sizeof(p));
printf("int*=%d\n", sizeof(int*));
printf("char*=%d\n", sizeof(char*));
printf("short*=%d\n", sizeof(short*));
printf("long*=%d\n", sizeof(long*));
printf("float*=%d\n", sizeof(float*));
printf("double*=%d\n", sizeof(double*));
system("pause");
return EXIT_SUCCESS;
}
指针类型在内存中大小:
指针指向变量和赋值操作:
2 野指针,空指针和万能指针
野指针、空指针与 万能指针
1、野指针:野指针是指向一个未知的内存空间,可能在读写的时候出现错误。
0-255都是系统保留的 不可以读,不可以写
2、空指针 没有指向任何的地址(其指向0的地址)
空制指针就是指向内存编号为零的空间,操作该内存空间会报错,一般情况空指针用于程序条件判断
3、万能指针:void * 指针可以指向任意变量的内存空间
指针指向变量和赋值操作
野指针:
int main()
{
int a = 10;
int* p = &a;
//指向内存编号为100的内存地址
//0-255都是系统保留的 不可以读,但是不可以写
//野指针是指向一个未知的内存空间,可能在读写的时候出现错误
p = 100;
*p = 100;
system("pause");
return EXIT_SUCCESS;
}
会报错
空指针:
int main()
{
int* p;
//空指针就是指向内存编号为0的空间,操作该内存空间会报错,一般情况空指针用于程序条件判断
p = NULL;
*p = 100;
printf("%d\n", *p);
if(p != NULL)
{
//free();
}
}
会报错
万能指针:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main01()
{
int a = 10;//4
// int* p=&a;
void* p = &a; //万能指针
*(int*)p = 100; //使用时得制定类型
printf("%d\n", a);
printf("%d\n", *(int*)p);
system("pause");
return EXIT_SUCCESS;
}
int main()
{
int arr[10] = { 0 };
void* p = arr;
*(int*)p = 100;//arr[0]; arr[1]
*((int*)p + 1) = 200;//arr[1]
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
3 const修饰指针
const修饰指针
1.通过指针修改const修饰的常量
const int a = 10;
int *p += &a;
*p = 100;
a =100;
可以通过1级指针修改一个常量的值
2.const修饰指针类型 int * 不能改变指针变量指向的内存地址的值 但是可以改变指针指向的地址
const int *p = &a
p=&b
*p = 100
可以改变指针指向的地址
3.const修饰指针变量 能改变指针指向地址的值,但不能改变指针指向的地址
int * const p = &a;
*p = 100;
p=&b
可以修改指针指向地址的值
4.const修饰指针类型修饰指针变量 不能改变指针指向的的值 也不能改变指针指向的地址
const int * const *p = &a;
*p =100;
p = &b;
const修饰指针类型
const修饰指针变量
1.通过指针修改const修饰的常量
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define LVL 100
int main()
{
//这种方式不安全,可以通过指针修改
//1.通过指针修改const修饰的常量
const int a = 10;
printf("%d\n", a);
int* p = &a;
*p = 100;
printf("%d\n", a);
printf("%d\n", *p);
system("pause");
return EXIT_SUCCESS;
}
2.如果const修饰int* 不能改变指针变量指向的内存地址的值,但是可以改变指针指向的地址
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define LVL 100
int main()
{
int a = 10;
int b = 20;
const int* p;
p = &a;
//但是可以改变指针指向的地址
p = &b;
//如果const修饰int* 不能改变指针变量指向的内存地址的值
//* p = 100;
printf("%d\n", *p);
system("pause");
return EXIT_SUCCESS;
}
3.const修饰指针变量,能改变指针变量指向地址的值,但不能改变指针指向的地址
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define LVL 100
int main()
{
int a = 10;
int b = 20;
int* const p = &a;
//但不能改变指针指向的地址
//p = &b;
//const修饰指针变量,能改变指针变量指向地址的值
* p = 100;
printf("%d\n", *p);
system("pause");
return EXIT_SUCCESS;
}
4.4.const 修饰指针类型,也修饰指针变量,不能改变指针变量指向的内存地址的值,也不能改变指针指向的地址
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define LVL 100
int main()
{
int a = 10;
int b = 20;
//4.const 修饰指针类型,也修饰指针变量,不能改变指针变量指向的内存地址的值,也不能改变指针指向的地址
const int* const p = &a;
//p = &b;
//*p = 100;
printf("%d\n", *p);
system("pause");
return EXIT_SUCCESS;
}
结论:
4 指针和数组,指针运算
指针和数组、指针运算:
1、数组名是数组的首地址,这是一个常量
2、指向数组的指针
格式int arr [10] = { 0 } ;
int * p = arr;
当操作指针的时候 间接操作了数组 arr[i] = p[i];
3、指针的降级操作 ,取当前地址的值:p[5]、*p
4、对指向数组的指针进行加减操作,可能会导致数组下标越界 。
5、相同的类型的指针相减 结果是两个指针相差的长度
区别:数组名通过sizeof可以求出数组大小,指针只包含数组的首地址信息
指针和数组关系
1.指向数组的指针,当操作指针的时候,间接操作了数组
2.指针的降级操作
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//数组名是数组的首地址,这是一个常量
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
//printf("%p\n", arr);
//printf("%p\n", &arr[0]);
//p = arr; [数据类型 *] 变量名
//1.指向数组的指针,当操作指针的时候,间接操作了数组
int* p = arr;
//2.指针的降级操作
//*p = 100;
//*(p + 1) = 100;
//p[5] = 300;
//*(p + 5) = 300;
//*p==arr[0]
//p = p+1;
*(p + 1) = 200;
p++;
*p = 200;
p[2] = 300;
arr[2] = 400;
for (int i = 0; i < 10; i++)
{
//*(p+i) //内存变了1*sizeof(int)
printf("%d\n", arr[i]);
//printf("%d\n", p[i]);//0 9
//printf("%d\n", *(p+i));
}
system("pause");
return EXIT_SUCCESS;
}
3.数组下标越界
p[10]=300
指针的运算
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
p++;
*p = 100;//{ 1,100,3,4,5,6,7,8,9,10 };
p = 100;//野指针
*p = 100;//非法操作野指针内存
p = &arr[9];
*p = 100;//{ 1,100,3,4,5,6,7,8,9,100 };
p--;
*p = 20;//{ 1,100,3,4,5,6,7,8,20,100 };
p = &arr[0];
*p += 100;//{ 101,100,3,4,5,6,7,8,20,100 };
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
system("pause");
return EXIT_SUCCESS;
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
p++;
*p = 100;//{ 1,100,3,4,5,6,7,8,9,10 };
//p = 100;//野指针
//*p = 100;//非法操作野指针内存
p = &arr[9];
*p = 100;//{ 1,100,3,4,5,6,7,8,9,100 };
p--;
*p = 20;//{ 1,100,3,4,5,6,7,8,20,100 };
p = &arr[0];
*p += 100;//{ 101,100,3,4,5,6,7,8,20,100 };
printf("%d\n", p);
p += 100;
printf("%d\n", p);
int len = p - arr;
printf("len = %d\n", len);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
system("pause");
return EXIT_SUCCESS;
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p;
//p = &arr[9];
//int len = p - arr;//9
//printf("%d\n", len);
for (p = arr; p < &arr[10]; p++)
{
//printf("%d\n", *p);
//printf("%d\n", p);
printf("%d\n", p[0]);
}
return 0;
}
5 指针冒泡排序
数组名和指针的区别:
数组名通过sizeof可以求出数组大小,指针只包含数组的首地址信息
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int arr[10] = { 4,9,10,3,5,7,1,8,2,6 };
//数组名和指针的区别,数组名通过sizeof可以求出数组大小,指针只包含数组的首地址信息
int* p = arr;
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(p));
return 0;
}
冒泡排序:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void bubble(int* p, int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
//*(p+j) =p[j]
if (*(p + j) < *(p + j + 1))
{
int temp = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = temp;
}
}
}
}
int main()
{
int arr[10] = { 4,9,10,3,5,7,1,8,2,6 };
//数组名和指针的区别,数组名通过sizeof可以求出数组大小,指针只包含数组的首地址信息
int* p = arr;
int len = sizeof(arr)/sizeof(arr[0]);
//printf("%d\n", sizeof(arr));
//printf("%d\n", sizeof(p));
bubble(p, len);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
system("pause");
return EXIT_SUCCESS;
}
6 实现strchr函数
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* mystrchr1(char* arr, char ch) //arr1的值不可改变
{
int i = 0;
while (arr[i] != '\0')
{
if (arr[i] == ch)
{
return &arr[i];
}
i++;
}
return NULL;
}
char* mystrchr2(const char* arr, char ch) //arr1的值不可改变
{
char* p = arr;
while (*p != '\0')
{
if (*p == ch)
{
return p;
}
p++;
}
return NULL;
}
int main()
{
char arr1[] = "hello world";
char ch = 'o';
//char* p = strchr(arr1, ch);
//char* p = mystrchr1(arr1, ch);
char* p = mystrchr1(arr1, ch);
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
7 字符串反转
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void rec(char* arr)
{
int len = strlen(arr);
//printf("%d\n",len);
//字符串的首地址
char* p1 = arr;
//字符串最后一个有效字符的地址
char* p2 = &arr[len - 1];
while (p1 < p2)
{
char temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2--;
}
}
int main()
{
char arr[] = "hello world";//dlrow olleh
rec(arr);
printf("%s\n", arr);
system("pause");
return EXIT_SUCCESS;
}
8 指针数组
指针数组
1、存储char *类型的地址数组
char * arr[] ={"hello","world","niao","baobei"};
2、指针数组,它是数组,数组的每个元素都是指针类型。
指针数组基本概念:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[] = { &a,&b,&c };
//arr[0]==&a;
//arr[1]==&b;
*arr[0] = 100;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
system("pause");
return EXIT_SUCCESS;
}
存储char *类型的地址数组:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//存储char *类型的地址数组
char* arr[] = { "hello","world","nihao","baobei" };
int len = strlen(arr[1]);
printf("%d\n", len);
printf("%c\n", *arr[0]);
printf("%c\n", *(arr[0]+1));
system("pause");
return EXIT_SUCCESS;
}
9 多级指针
多级指针:
1、如果二级指针前面加一个* 代表一级指针的值
2、二级指针前面加** 代表指针指向一级指针指向地址的值,加*降维度
3、如果n级指针在前面加n个*就是指针指向一级指针指向地址的值
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int a = 10;
int* p = &a;
int** pp = &p;
//二级指针,前面加*代表一级指针的值
*pp = &a;
//二级指针前面加**,代表指针指向一级指针指向地址的值
**pp = 20;
system("pause");
return EXIT_SUCCESS;
}
多级指针推论:
10 指针和函数
1.值传递不会改变实参的值
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void tab(int a, int b)
{
int temp = a;
a = b;
b = temp;
printf("%d %d\n", a, b);
}
int main()
{
int a = 10;
int b = 20;
tab(a, b);
printf("%d %d\n", a, b);
system("pause");
return EXIT_SUCCESS;
}
函数值传递的执行过程:
2.地址传递可以改变实参的值
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void tab(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
printf("%d %d\n", *a, *b);
}
int main()
{
int a = 10;
int b = 20;
tab(&a, &b);
printf("%d %d\n", a, b);
system("pause");
return EXIT_SUCCESS;
}
地址传递:
11 指针作为函数参数
1、地址传递可以改变实参的值
2、函数参数中如果有数组 都会 转化为指针 sizeof(数据类型) 所以求出来的值不作为循环的值使用
3、两种方式可以求出字符串长度 \0 (图1)
①int len = strlen(arr)
②int i = 0;
1.函数参数中如有数组,都会转化为指针,sizeof(int*)=4,所以求出来的值不能作为数组的循环条件存在,所以要传长度
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void print(int arr[10])
{
// 函数参数中如有数组,都会转化为指针,sizeof(int*)=4,所以求出来的值不能作为数组的循环条件存在
for (int i = 0; i < sizeof(arr); i++)
{
printf("%d\n", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,6,0,7,8,9,10 };
print(arr);
system("pause");
return EXIT_SUCCESS;
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void print(int * arr,int len)
{
for (int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,6,0,7,8,9,10 };
print(arr,sizeof(arr)/sizeof(arr[0]));
system("pause");
return EXIT_SUCCESS;
}
2.字符串数组传递
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//1.数组作为函数参数可以退化为指针
//2.在传递数组时需要加上数组的个数
void print(char * arr)
{
//两种方式可以求出字符串长度 \0
int len = strlen(arr);
int i = 0;
while (arr[i] != '\0')
{
i++;
}
}
int main()
{
//char arr[] = "hello world";
//print(arr);
char arr[] = { 'h','e','l','l','o' }; //这个要传递个数
system("pause");
return EXIT_SUCCESS;
}
12 函数的返回值是指针
字符数组,创建位置在栈区
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* test()
{
//字符数组,创建位置在栈区
char arr[] = "hello world";
return arr;
}
int main()
{
char* p = test();
printf("%p\n", p);
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
字符串常量,会在程序运行时,创建在常量区
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* test()
{
//字符数组,创建位置在栈区
//char arr[] = "hello world";
//字符串常量,会在程序运行时,创建在常量区,不能被修改
char* arr = "hello world";
return arr;
}
int main()
{
char* p = test();
printf("%p\n", p);
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
13 实现strstr函数
#include<stdlib.h>
/*
1.两个匹配的字符串,必须完全匹配,匹配个数=字符串长度
2.如果匹配一个字符串,需要记录别匹配字符串地址
3.如果匹配一个半成功,回到记录被匹配字符串地址+1
4.如果匹配的被匹配字符串的结尾,匹配个数不等于字符串长度
*/
char* mystrstr(char* dest, char* src)
{
int i = 0;
int j = 0;
//匹配个数
int count = 0;
int len = strlen(src);
char* p=NULL;
while (dest[i] != '\0')
{
while (dest[i] == src[j])
{
if (!count)
//如果匹配成功一个字符,需要记录位置
p = &dest[i];
count++;
i++;
j++;
//匹配成功
if (count == len)
{
return p;
}
}
//发生改变的值 i j count p
if (count < len)
{
i = i - count;
j = 0;
count = 0;
}
i++;
}
return NULL;
}
int main()
{
//char* p = strstr("hello world", "llo");
char* p = mystrstr("hello world", "llo");
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
strstr优化
#include<stdlib.h>
/*
1.两个匹配的字符串,必须完全匹配,匹配个数=字符串长度
2.如果匹配一个字符串,需要记录别匹配字符串地址
3.如果匹配一个半成功,回到记录被匹配字符串地址+1
4.如果匹配的被匹配字符串的结尾,匹配个数不等于字符串长度
*/
char* mystrstr(char* dest, char* src)
{
char* p=NULL;
char* temp = src;
while (*dest)
{
p = dest;
while (*dest == *temp)
{
dest++;
temp++;
}
if (!*temp)
return p;
else
temp = src;
dest = p;
dest++;
}
return NULL;
}
int main()
{
//char* p = strstr("hello world", "llo");
char* p = mystrstr("hello world", "llo");
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
14 指针和字符串
6、指针和字符串
①*p改变字符串中的值
②字符串与指针在程序中打印的值:
③字符串常量 是一个常量的数组 可以读取字符和字符串 但是不能修改
字符串数组:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
char arr[] = "hello world"; //栈区
char* p;
p = arr;
*p = 'A';//arr[0] p[0]
p++;
*p = 'B';//arr[1] p[1]
printf("%s\n", p);
printf("%s\n", arr);
printf("%d\n", sizeof(arr)); //12 包含\0
printf("%d\n", strlen(arr)); //11
printf("%d\n", sizeof(p)); //4
printf("%d\n", strlen(p)); //10
system("pause");
return EXIT_SUCCESS;
}
字符串常量:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
char* arr = "hello world";//常量区
printf("%s\n", arr);
printf("%c\n", arr[0]);
char* p = arr;
printf("%p\n", p);
//字符串常量是一个常量的数组,可以读取字符或者字符串,但不能修改
//p[0] = 'A';//报错
//*p = 'A';//报错
p = "hello world";
//p[0] = 'A';//报错
//*p = 'A';//报错
printf("%p\n", p);
system("pause");
return EXIT_SUCCESS;
}
15 实现strcat函数
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void mystrcat(char* arr, char* s1)
{
//while (*arr)
// arr++;
//while (*arr++ = *s1++);
while (*arr)
arr++;
while (*s1)
{
*arr = *s1;
arr++;
s1++;
}
*arr = '\0';
}
int main()
{
char arr[100] = "hello";
char* s1 = "world";
mystrcat(arr, s1);
printf("%s\n", arr);
system("pause");
return EXIT_SUCCESS;
}
16 字符串排序
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void bubble(char** arr,int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
//比对两个字符串的首字母
/*
int *p =&a;
*(p+j)
*/
//1.指针判断
/*
if (**(arr+j) < **(arr +j+1))
{
char* temp = *(arr+j);
*(arr+j) = *(arr +j+ 1);
*(arr+j + 1) = temp;
}
*/
//2.数组判断
/*
if (*arr[j]<*arr[j+1])
{
char* temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
*/
//3.混合判断
if (arr[j][0] < arr[j + 1][0])
{
char* temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
char* arr[] = { "dstudent","btree","cnew","abee" };
bubble(arr, 4);
for (int i = 0; i < 4; i++)
{
printf("%s\n", arr[i]);
}
system("pause");
return EXIT_SUCCESS;
}