定义字符指针,分别指向堆区空间,计算字符串的长度
要求:
1.定义函数实现堆区空间申请
2.在主函数中实现输入字符串
3.定义函数实现字符串长度 size_t my_strlen(const char *s)
4.定义函数释放堆区空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *create( )
{
char *p=(char *)malloc(sizeof(char)*100);
if(p==NULL)
return NULL;
else
return p;
}
char *Input(char *str)
{
char *p=gets(str);
return p;
}
size_t my_strlen(const char *str)
{
const char *p=str;
while(*str)
str++;
return str-p;
}
void Output(char *str)
{
printf("%s\n",str);
}
char *free_space(char *p)
{
if(p==NULL)
return NULL;
else
{
free(p);
p=NULL;
}
return p;
}
int main()
{
printf("请输入一段字符: ");
char *p=create();
p=Input(p);
Output(p);
printf("该字符串长度为:%ld\n",my_strlen(p));
p=free_space(p);
return 0;
}
结果为:
一、存储类型
a)static
1.static修饰未初始化全局变量,默认结果为0
2.static修饰局部变量,延长生命周期,生命周期不是作用域
依旧是局部变量
int a=0;//全局变量 生命周期和作用于都是从定义开始到整个文件结束
void fun()
{
int b=0;//局部变量
static int c=0;//局部变量 作用于:在fun函数使用
printf("&c=%p\n",&c);
a++;
b++;//1 1
c++;//1 2 3
printf("a=%d\n",a);//1 2 3
printf("b=%d\n",b);//1 1 1
printf("c=%d\n",c);//1 2 3
}
int main(int argc, const char *argv[])
{
fun();
fun();
fun();
printf("c=%d\n",c);
return 0;
}
3. static修饰函数只能在当前文件中调用,不可以跨文件调用
4.static修饰其他文件的全局变量,不可以使用extern引用
5.static修饰指针不可以指向auto类型的地址
原因:计算机先为静态变量分配空间,后再分配auto类型的变量
不可以使用指针指向不存在的变量地址。
b) extern
作用:引用外部变量
c)const 不是存储类型,修饰变量
作用:修饰的变量不发生改变
1.const修饰的全局变量,值不变,变量的空间在静态区的只读段
2.const修饰的局部变量,值不变,变量的空间在栈区
3.const和指针
const int *p *在const的右边,修饰的值,值不变,地址可以改变
int const *p *在const的右边,修饰的值,值不变,地址可以改变
int * const p *在const的左边,修饰的地址,地址不变,值可以改变
const int * const p 第一个const修饰的值,第二个const修饰的地址
地址和值都不可以改变
int const * const p 第一个const修饰的值,第二个const修饰的地址
地址和值都不可以改变
d) auto
自动类型
1.当默认不写存储类型时,默认就是auto类型
2.auto不可以直接修饰全局变量【auto int a】,定义全局变量时,默认是auto类型,但是不能是auto修饰,空间在静态区,从定义开始到整个文件结束
可以使用extern引用
3.auto修饰局部变量时,空间在栈区,从定义开始到本函数有效
不可以返回一个局部变量的地址
e) register 是存储类型之一
作用:表示这是一个寄存器变量
运行速度:寄存器> cache(高速缓存) > 内存
特点:不可以进行寻址操作
f) volatile 不是存储类型
作用:防止内存优化,保持内存的可见性
二、虚拟内存空间
1.栈区:栈的思想,先进后出,后进先出,先定义变量后分配空间
导致栈的地址分配值从大地之到小地址
2.堆区:满足队列的思想,先进先出,后进后出,先定义变量先分配空间,导致堆区的地址分配从小地址到大地址。
3,栈区和堆区没有边界点,当栈区地址和堆区地址临界,那就是边界
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int a=10;//全局 已经初始化 在.data段
int b;//全局 未初始化 在.bss段
const int c=10;//sonst修饰的全局变量在.ro
char str[]="hello"//全局 初始化str在.data
//"hell0" 在.ro段,
//字符串hello赋值一份给str
//可以修改
char *p="hello"//全局 p在.data段
//hello在.ro段a
//p指向只读段的hello常亮的首地址
int *p=(int *)malloc(100);//报错 全局不可以调用函数
static int d;//静态区 未初始化 .bss段
static int e=10;//静态去 初始化 .data段
int main(int argc, const char *argv[])
{
static int d; //静态局部变量 静态区 未初始化 .bss段
static int e=10; //静态局部 初始化 .data段
int a=10; //栈区
int b; //栈区
const int c=10;//const修饰的局部变量在栈区
char str[]="hello"//str:栈区 hello只读段
char *p="hello"//p在栈区
int *p=(int *)malloc(100);//p在栈区 100在堆区
return 0;
}
动态申请:
头文件:#include <stdlib.h>
格式:void *malloc(size_t size);
参数:size_t size 表示堆区申请空间字节大小
返回值:void *,如果申请成功则返回堆区空间首地址
如果申请失败返回NULL,记得保存堆首地址
0x10--0x20
0x16--0x20
使用格式:
申请单个空间
int *p=(int *)malloc(sizeof(int))
申请连续空间
int *p=(int *)malloc(sizeof(int)*n)
动态释放:
头文件:#include <stdlib.h>
格式: void free(void *ptr);
参数:void *ptr 表示释放的指针
返回:无返回
使用格式:
int *p=(int *)malloc(sizeof(int)*n)
//0x10--0x50 p=0x10
free(p); //p=0x10
p=NULL;//防止野指针
野指针
1,未初始化的指针直接使用,称为野指针
int *p;//p指向随机地址
printf("*p=%d\n",*p);
2.返回一个局部变量的地址,称为野指针
int *fun()
{
int a;//0x20-0x23 局部变量,当调用函数分配空间,
//函数调用结束空间释放
return &a;
}
3.通过指针越界访问数组
int arr[3];
int *p=arr;
*(p+3)=100;
4.指针指向堆区空间,释放指针,此时指针为野指针
int *p=(int *)malloc(sizeof(int)*n)
//0x10--0x50 p=0x10
free(p); //p=0x10
四、宏
i)宏定义
格式: #define 宏名 宏体
不可以修改,常量,只做替换不做计算,不做正确性检查
1.宏定义
#define N 10
#define M 'A'
#define K 1.1
#define STR "hello"
2.宏函数
格式: #define 宏函数名(参数列表) 宏体
宏函数名:大写
参数列表: 宏函数的参数列表,不需要加数据类型
宏体不需要加{}
#define MAX(a,b) a>b?a:b
#define SUM(a,b) a+b
#define SUM(a,b) (a+b)
ii)宏函数
#if 宏条件
c语句1
#else
c语句2
#endif
如果宏条件成立则执行C语句1,否则执行C语句2
多用于注释
#ifndef 宏名
c语句1
#endif
如果宏名未定义,则执行C语句1
#ifdef 宏名
C语句;
#endif
如果宏名已经定义,则执行C语句1
iii)多文件编译
头文件:预处理命令,函数声明,全局变量
主函数:存储主函数
自定义函数
执行过程:gcc main.c test.c
五、typedef
作用:类型重定义,起别名
格式: typedef 数据类型 别名
1.别名:满足命名规范
2.typedef是关键字,是C语句,加分号
typedef int size_4;//把int起别名size_4
int==》size_4
int a;
size_4 a;
int a;
int arr[3];
int arr[3][4];
char str[];
char str[][];
int *p
int **p
int *p[];
int (*p)[]
int *p(void)
int (*p)(void)
int (*p[])(void)
数据类型
int ;
int [3];
int [3][4];
char [];
char [][];
int *
int **
int *[];
int (*)[]
int *(void)
int (*)(void)
int (*[])(void)
typedef 数据类型结合
typedef int size_4;//size_4是类型名int
typedef int arr_t[3] ;//arr_t是int [3]的类型名
typedef int arr[3][4];//arr是int [3][4]的类型别名
typedef char str[];
typedef char str[][];
typedef int *p
typedef int **p
typedef int *p[];
typedef int (*p)[]
typedef int *p(void)
typedef int (*p)(void)//p函数指针类型名
typedef int (*p[])(void)
5.1 typedef和#define之间的区别
#define size_4 int ----》 typedef int size_4
1,typedef属于C语句,宏不属于C语句
2,typedef在编译阶段,宏在预处理阶段
3,typedef是类型重定义,宏属于宏替换
4,typedef可以适用于任何复杂的数据类型,但是宏只能适用于基本类型的替换
typedef int arr[3] 正确
宏实现不了