C编程基础day08


return 函数在主函数中return结束程序。在其他函数中return结束该函数,但程序仍运行。
exit 在任何函数中执行都会结束程序,也就是结束进程。

如果函数不是写在main函数前边,那么需要在函数被调用的前边某个地方声明。
一个函数可以声明多次,但只能定义一次,声明的变量名称和定义的变量名称可以不一样。甚至声明时候可以只写形参类型,不写形参名字。

分文件编程: 按功能来分,不同的功能写在不同的文件中。XXX.h、XXX.c
一次性编译多个.c文件办法, 某个函数在一个文件定义,另外一个文件中声明这个函数后,就可以调用了。
gcc main.c mystrlen.c -o test或者gcc *.c -o test

多个文件中不能出现同名函数,static除外。
函数定义内容不写到.h中原因是:可能一个.h会被多个.c文件调用,会造成一个函数在多个.c中被定义了,所以出错。
函数的声明一般写到.h中,.h文件中可以声明多个函数,调用的主函数可以只写一句#include"XXX.h"即可,不用多写几句函数声明。
另外函数声明写到.h文件中,虽然 可能一个.h会被多个.c文件调用,会造成一个函数在多个.c中被定义声明了,但是函数本身就可以多次声明,但只能一次定义。

同一个文件如何避免对同一个.h文件同时包含:
1、在头文件地方加上 #pragma once   
2、#ifndef XXX_H
     #define XXX_H
      头文件正文
    #endif


a.out存在于硬盘(ROM)中, 我们运行a.out时候把程序加载到内存(RAM),运行着的程序叫进程。这里的内存也就是内存条。内存是沟通CPU和硬盘的桥梁

栈地址分配时候从高到低递减找到合适的大小分配。
1、内存是以1字节为单位的
2、每个字节的内存都有标号,这个标号就是地址,也就是指针。
3、地址需要存储, 32位编译器使用32位(4字节)存储此地址
                              64 位编译器使用64位(8字节)存储此地址  
4、每个存储单元(1字节)分配一个号码,就叫编码。
5、根据地址找到对应内存,也叫寻址。
char ch;
int a=10;


ch /*ch占1字节*/ /*高地址*/
a /*a占4字节*/  
a /*a占4字节*/  
a /*a占4字节*/  
a /*a的首地址=&a*/ /*低地址*/

指针就是地址,地址就是指针。
指针变量时存放地址的变量,习惯上叫做指针。
1、指针也是一种数据类型。 
int * 也是一种类型, int * p; p也是一个变量,p的类型为int *, 可以对p进行赋值。
p=(int *)123;或者 p = 123;
printf("%p",p);

gcc hello.c -w 忽略打印

2、指针指向谁,就把谁的地址赋给指针。
int a=10;
int * p;
p = &a;
printf("%p",p);和printf("%p",&a);结果一样

3、直接操作指针变量p没意义,
4、操作*p代表操作指针所指向的内存, *p代表a。
*p=100;
printf("%d",*p);和printf("%d",a);结果一样

*符号有两层含义: 
1、在定义变量时,*代表类型,它是指针类型 如 int *p;
2、在使用变量时, *代表操作指针指向的内存。如 *p=100;

编译时候段错误一般都是内存出错。

野指针:这个指针变量保存了一个没有意义的地址(非法地址)。
操作野指针变量本身没有问题,但是操作野指针所指向的内存才导致段错误。

关于空指针两个好的习惯:
1、定义指针时候立马给这个指针赋值为空指针NULL;
2、给指针指向地址赋值之前最好检查一下,确认指针不是空指针。

指针大小:
32位编译器用32位(4字节)大小保存地址大小
64位编译器用64(8字节)位大小保存地址大小
VS若想测试64位编译器可以在Debug下拉三角下配置一下编译管理器,活动平台选择X64。

多级指针:
如果定义一个合适类型的变量保存另一个变量的地址。在需要保存变量地址类型的基础上加一个*号。
int a=10; 
int *p=&a;
int * *q = &p;// 保存p的地址,所以在p的类型int *基础上再加一个*号变为int **;
int ** *t = &q;// 保存q的地址,所以在q的类型int **基础上再加一个*号变为int **  *;
int *** *m = &t;// 保存t的地址,所以在t的类型int ***基础上再加一个*号变为int *** *;

*m代表t的值,所以*m==t==&q
**m代表*t,*t代表q,即 **m代表q, 所以**m==*t==q=&p;
***m代表**t,**t代表*q, *q代表p, 即***m代表p,所以***m==**t==*q=p=&a;
****m代表***t,***t代表**q, **q代表*p, *p代表a ,即 ****m代表a, 所以****m==***t==**q=*p=a;
总结:多级指针不是看多少星,而是看什么类型的变量。
1、指针变量也是一个变量, 是变量就可以进行赋值。
2、指针指向谁,就把谁的地址赋给指针
3、*p操作的是指针指向的内存。

[]不仅仅代表数组 ,*p等价于*(p+0)同时等价于p[0].  实质上内存是一个大数组,能用指针的地方就能用数组。 *(p+i)等价于p[i]
int a=10;
int *p = &a;
printf(“%d %d %d”,a, *p, p[0]);

万能指针: void *
1、不能编译定义 void类型的变量,因为无法确定类型,编译器不知道类型就不知道分配多少字节内存。
2、可以定义void *类型变量,因为指针大小是确定的,编译器是多少位的,指针大小占用内存就是多少位的。
3、void *可以指向任何类型的变量。使用指针所指向的内存时,最好转换为它本身的指针类型。
void *p=NULL;
int a=10;
p=&a;  //err因为这样话编译器虽然知道首地址但不知道*p到底操作多大内存,若是int *则从首地址开始操作4字节, 若是char *则从首地址开始操作1字节。
*  ((int *)p)  =222; //先将p指针变量强制转化为(int *),然后再对p指向的内存进行操作,这次知道是操作4字节。
printf("*p =%d \n", *( (int*)p)  );

指针步长; int *p; p+1和p值相差为4(int *指针步长为4), 因为*p操作的是int型变量,每次操作需要4个字节。同理 char *q的指针步长为1.

const 修饰的指针变量。
int a=10;
const int *p =&a; //const修饰的是*(const从左往右数 跳过int 修饰的是*),代表指针所指向的内存是只读。
*p=200;// err编译不通过。
p=NULL;//OK,这条语句可以编译通过

int const * p2 =&a; //const修饰的还是是*,代表指针所指向的内存是只读。
*p2=300;// err 同样编译不通过,
p2=NULL;//OK,这条语句可以编译通过

int  * const p3 =&a; //const修饰的是指针变量p3,代表指针变量的值是只读。
*p3=400;// OK, 这条语句可以编译通过
p3=NULL;//err编译不通过。 p3是常量指针,指向的地址不能改,指向哪就是指向哪。 类似于数组名,数组名的地址也不能改。

const int  * const p4 =&a; //这个什么都改不了, 变量不能改, 指针指向的地址不能改。

数组名是常量,数组名类似于常指针,其指向首地址,指向的地址不能改。

int a[5]={1,2,3,4,5};
int *p = NULL;
p =a; 或者p= &a[0];
for(int i=0; i<5; i++)
{
    printf("%d %d %d %d\n",a[i],*(a+i), *(p+i), p[i]);
}

1、不使用排序找出第二大的数
2、不用库函数实现字符串拷贝功能。




猜你喜欢

转载自blog.csdn.net/Shayne_Lee/article/details/81050990
今日推荐