C语言学习笔记(一):程序编译执行过程及指针基础
1、程序编译执行的过程
1、预处理:宏定义展开、头文件展开、条件编译等,通过是将代码中的注释删除,这里并不会检查语法
2、编译:检查语法,将预处理后的文件编译生成汇编文件
3、汇编:将汇编文件生成目标文件(二进制文件)
4、链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
2、程序在存储空间中的流转
之后详细学习内存及cpu的内部结构
3、cpu与内存
1、64与32位系统的区别 如下图
2、寄存器、缓存、内存三者关系 如下图
4、指针
1、指针基础
//指针的定义 :
int a=10;
int* p=&a;
*p=20;
其中变量 a 指针p都是有自己的内存空间比如a的内存空间为0xff00,指针p的内存空间为0xff04。
int a=10 的操作表示开辟内存空间地址为0xff00并在里面存储了一个int类型的值10,
int* p=&a;的操作表示开辟内存空间地址为0xff04并在里面存储变量a的内存空间地址0xff00
*p=20;的操作表示 将变量a的内存里面的值变更为20;
其中 星号表示取指针指向的内存空间里面的值(用于指针),&表示取内存空间的地址。
无论是什么类型的指针,里面存的都是地址,内存地址都时是无符号的整型,所以都是4(在32位系统中为4,在64位系统中为8个字节)个字节大小
2、野指针、空指针、万能指针
内存中0-255的控件是系统保留的 不能读也不能写。
野指针是指向的一个位置的内存空间 可能在读写的时候出现错误。
int* p;
p=NULL;
*p = 100;
空指针就是指向内存空间编号为0的空间,操作空指针程序同样会报错
上面定义了一个指针 并且给指针赋值为空,然后给空指针指向的内存赋值100,这样程序会挂掉。
一般空指针是用来进行判断的,比如我们申请一块内存空间 并将内存空间的地址赋值给空指针,然后判断空指针的值是否为空,从而来判断申请内存空间的操作有没有成功。
万能指针如下:
int main(){
int arr[10] = {0};//创建数组里面10个值 全为0
void* p=arr;//这里没有用取地址符号&,是因为数组的变量名指向的就是数组的首地址
*(int*)p = 100;//void*表示万能指针 若想给指针指向的内存赋值 得先将指针转换为指定的类型(int*)表示将万能指针变量p转换为整型指针变量
*((int*)p+1)=200; //表示给arr[1]赋值
}
3、const修饰指针
const int a =10;
int* p =&a;
*p =100;
在程序中const常量 我们是不希望他的值发生变化的,但是上面代码中利用指针 竟然改变了const常量 a的值,这种情况是不安全的。
int a =10;
const int* p =&a;
*p =100; //程序在这里就会报错
用const修饰指针变量的情况下 这个指针指向的内存空间里的值就不能被改变了,但是指针本身内存空间里的值是可以改变的,不然 的话上面代码第二行就会报错。
上图中用方框 框起来的代码都是会报错的,const可以认为是一把锁 锁在谁的前面 谁就不能动。
比如锁在指针变量p的前面( int* const p),那么指针变量p 自己的内存空间就无法修改,也就是p=&a;会报错。
比如锁在int前面 也在p前面 (const int const p=&a)这样p=&b,*p=100 这两个操作都无法进行,也就是说 再也无法通过指针p去操作变量a 所在的内存空间的值,且指针p永远指向变量a的内存空间。
4、指针实现冒泡排序
#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++)
{
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 };
int* p = arr;
int len = sizeof(arr) / sizeof(int);
bubble(p, len);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
}
5、实现strchr函数
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* mystrchr(char* p, char ch)
{
int index = 0;
while (*p!='\0')
{
if (*p=='o')
{
return p;
}
p++;
}
return NULL;
}
int main02()
{
char arr1[] = "hello world";
char ch = 'o';
char* p = mystrchr(arr1, ch);
printf("%s\n", p);
/*printf("%c\n", *(p+sizeof(char)));
printf("%c\n", *(p + 1));
printf("%c\n", *(p + 2));*/
return 0;
}
6、实现字符串反转
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void reconvert(char* arr)
{
int len = strlen(arr);
char* p1 = arr;
char* p2 = &arr[len - 1];
while (p1 < p2)
{
char temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2--;
}
}
int main03()
{
char arr[] = "hello world";
reconvert(arr);
printf("%s\n", arr);
return 0;
}
7、指针数组
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main04()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[] = {&a,&b,&c};
*arr[0] = 100;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
return 0;
}
int main()
{
char* arr[] = { "hello","world1","nihao12","baobei22" };//指针数组arr里面存的是 各个字符串的首地址
int len = strlen(arr[1]);
printf("%d\n", len);
printf("%c\n", *(arr[1] + 2));
}
8、多级指针
n级指针的值为n-1级指针的内存地址。
由程序的运行结果来看 二级指针的值为一级指针地址的时候,*pp:指向的是一级指针的值;**pp:指向的是 一级指针指向的地址里面存的值。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int a = 10;
int* p = &a;
int** pp = &p;
/**pp = 20;*/
printf("a的值:%d\n", a);
printf("a的地址:%p\n", &a);
printf("p的值:%p\n", p);
printf("p的地址:%p\n", &p);
printf("pp的值:%p\n", pp);
printf("pp的地址:%p\n", &pp);
printf("--------------------------------------\n");
printf("*pp指向的值:%p\n", *pp);
printf("**pp指向的值:%d\n", **pp);
return 0;
}
总结:*pp==p==&a
**pp==*p==a=10