1.typedef使用
1.1起别名 - 简化struct关键字
1.2区分数据类型
1.3提高代码移植性
#define _CRT_SECURE_NO_WARNINGS // VS下使用传统库函数会建议用_s更安全函数,如果不用会报错误 C4996
#include<stdio.h> //标准 i input 输入 o output 输出
#include<string.h> //对字符串处理 strcat strstr strcmp strcpy
#include<stdlib.h> //malloc(在堆区开放内存) free
/* 1.typddef使用 */
/*1.可以起别名*/
struct Person
{
char name[64];
int age;
};
//语法 typedef 原名 别名
typedef struct Person myPerson;
/*或者把前面的代码改成
typedef struct Person
{
char name[64];
int age;
}myPerson;
*/
void test01()
{
struct Person p = { "aaaa",10 }; // 原来的调用方式
myPerson p2 = { "bbbb",11 }; // 后来的调用方式
}
/* 2.区分数据类型,防止数据类型混淆*/
void test02()
{
typedef char* PCHAR;
PCHAR p1, p2; // p1,p2是指针类型
//char* p1, p2; // p1是指针,p2是char类型
char* p3, * p4; // 两个都是指针
}
/* 3.提高移植性,现在的用法比较少*/
typedef long long MYINT;
void test03()
{
//long long a = 10;
//long long b = 100;
// 早期编译器没有long long类型,可以把它改成MYINT类型
MYINT a = 10;
MYINT b = 100;
}
//main 函数 程序入口
int main()
{
//char buf[1024];
//strcpy(buf, "hello world");
//printf("%s\n", buf);
system("pause"); //阻塞 请按任意键继续
printf("aaaaaaaaaaaaaa"); // 有了上面那句下面这句会短暂的出现一次
return EXIT_SUCCESS; //返回成功退出 0
}
VS创建多项目:
2.void使用
2.1不可以利用void创建变量 无法给无类型变量分配内存
2.2用途:限定函数返回值,函数参数
2.3void * 万能指针 可以不通过强制类型转换就转成其他类型指针
/* 1.void 无类型,不可以通过void创建变量,原因无法给void无类型变量分配内存 */
void test01()
{
//void a = 10;
}
/* 2.用途:限定函数返回值,限定函数参数 */
void test02()
{
return 10;
}
/* 3. (void * 万能指针 不管是几级指针,不管是什么类型的指针都是4个字节 //**/
void test03()
{
printf("size of void* =%d\n", sizeof(void*)); // 4
int * pInt = NULL;
char * pChar = NULL;
pChar = (char *)pInt; // 没有强制转换会有警告
void* p = NULL;
pChar = p; // 没有警告 万能指针可以不通过强制类型转换就转成其他类型指针
}
//main 函数 程序入口
int main()
{
test02(10); // 函数里没有参数,这样写会没警告没错误,函数里面有void就会出警告
printf("%d\n", test02()); // test02函数前有void 这句就会报错,要是int或者没有就会报错
test03(); // 不会报错
system("pause"); //阻塞 请按任意键继续
printf("aaaaaaaaaaaaaa"); // 有了上面那句下面这句会短暂的出现一次
return EXIT_SUCCESS; //返回成功退出 0
}
3.sizeof用法
3.1本质:不是一个函数,是一个操作符
3.2返回值类型 unsigned int无符号整型
3.3用途:可以统计数组长度
/* 1. sizeof本质 是不是一个函数? 不是函数,而是操作符(类似于加减乘除) */
void test01()
{
printf("size pf int = %d\n", sizeof(int)); //4 这里必须加括号
double d = 3.14;
printf("size of d = %d\n", sizeof d); // 8 这里括号可以去掉,如果是函数就必须加括号,这里可以不加可见不是函数
}
/* 2. sizeof返回值类型 无符号整型 */
void test02()
{
//unsigned int a = 10;
当unsigned int和int做运算,会转换成统一unsigned 数据类型,所以最终输出大于0
//if (a - 20 > 0)
//{
// printf("大于0\n");
//}
//else
//{
// printf("小于0\n");
//}
// 输出大于0
if (sizeof(int) - 5 > 0)
{
printf("大于0");
}
else
{
printf("小于0\n");
}
}
/* 3. sizeof用途:统计数组长度,符号类型长度 */
// 当数组名做函数参数时,会退化为指针,指向数组中第一个元素的位置
void caclAray(int arr[])
{
printf("array length =%d\n", sizeof(arr));
}
void test03()
{
int arr[] = { 1,2,3,4,5,6,7,8 };
printf("array length =%d\n", sizeof(arr)); // 32
caclAray(arr); // 4
}
4.变量的修改方式
4.1直接修改
4.2间接修改
4.3对自定义数据类型做练习
// 变量的修改方式
void test01()
{
//1、直接修改
int a = 10;
a = 20;
//2、间接修改
int* p = &a;
*p = 30;
printf("a = %d\n", a);
}
// 自定义数据类型的练习
struct Person
{
char a; // 0~3 因为
int b; //4
char c; //8
int d; //12
};
void test02()
{
struct Person p = { 'a',10,'b',20 };
// 直接修改 d 属性
p.d = 1000;
// 间接修改 d属性
struct Person * pp = &p;
/*pp->d = 1000;*/
printf("%d\n", pp);
printf("%d\n", pp + 1); // 比上面多了16
char* pp = &p;
*(int*)(pp + 12) = 2000; // 通过指针的迁移来改变d属性
*(int*)((int *)pp + 3) = 2000; // 和上面相同
}
5.内存分区
5.1运行前
5.1.1代码区 共享 只读
5.1.2数据区 存放数据:全局变量 、静态变量、常量
5.1.2.1已初始化数据区 data
5.1.2.2未初始化数据区 bss
5.2运行后
5.2.1栈 符合先进后出数据结构,编译器自动管理分配和释放,有限容量
5.2.2堆 容量远远大于栈,不是无限。手动开辟 malloc 手动释放 free
6.栈区
6.1符合先进后出数据结构
6.2注意事项:不要返回局部变量的地址,局部变量在函数执行之后就被释放了,释放的内存就没有权限取操作了,如果操作结果未知
int* myFunc()
{
int a = 10; // 分配在栈上
return &a;
}
void test01()
{
// 局部变量a早已被释放,因此我们没有权限操作这块内存空间
int* p = myFunc(); // 这里函数结束之后,内存就被释放了,所以无法获得内存信息
printf("* p = %d\n", *p); // 这里输出结果是对的,是因为做了优化
printf("* p = %d\n", *p);
/*
* p = 10
* p = -858993460*/
}
char* getString()
{
char str[] = "hello world";
return str;
}
void test02()
{
char* p = NULL;
p = getString();
printf("%s\n", p); // 结果未知 , 因为之前栈上的内存已经被被释放了
}
7.堆区
7.1利用malloc在堆区创建数据
7.2利用free释放堆区
7.3注意事项:主调函数没有分配内存,被调函数需要用更高级的指针去修饰低级指针,进行分配内存
int* getSpace()
{
int * p = malloc(sizeof(int)*5);
if (p == NULL)
{
return;
}
for (size_t i = 0; i < 5; i++)
{
p[i] = i + 100;
}
return p;
}
void test01()
{
int* p = getSpace();
for (size_t i = 0; i < 5; i++)
{
printf("%d\n", p[i]); // 这里可以正确打印,因为这是在堆区开辟的内存,不会释放
}
// 手动开辟 手动释放
free(p);
p = NULL;
}
/*************************** 注意事项 **************************/
void allocateSpace(char* pp)
{
char* temp = malloc(100);
memset(temp, 0, 100); // 置空
strcpy(temp, "hello world");
pp = temp;
}
void test02()
{
char* p = NULL;
allocateSpace(p);
printf("%s\n", p); // (null)
}
void allocateSpace2(char** pp)
{
char* temp = malloc(100);
memset(temp, 0, 100); // 置空
strcpy(temp, "hello world");
*pp = temp;
}
void test022()
{
char* p = NULL;
allocateSpace2(&p);
printf("%s\n", p); // 输出正确结果
}
8.static 和extern 区别
8.1特点:在运行前分配内存,程序运行结束生命周期结束 ,在本文件内都可以使用静态变量
8.2extern 可以提高变量作用域
int g_a = 1000; //在C语言下,全局变量前都隐式加了关键字 extern
/*
static 静态变量
特点:在运行前分配内存,程序运行结束生命周期结束,在本文件内都可以使用
*/
static int a = 10; // 全局作用域
void test01()
{
static int d = 20; // 局部作用域 a和b生命周期相同,但是作用域不相同
}
// 如果别的代码是g_b,这里extern int g_a, 会出现“1个无法解析的外部命令错误”,是链接阶段出现的错误
// 告诉编译器,下面代码中出现g_a不要报错,是外部链接属性,在其他文件中
extern int g_a;
printf("%d\n", g_a);
9.常量
9.1const修饰的变量
9.1.1全局变量
9.1.1.1直接修改 失败 ,间接修改 语法通过,运行失败,受到常量区保护
9.1.2局部变量
9.1.2.1直接修改 失败 , 间接修改成功,放在栈上
9.2字符串常量
9.2.1vs 将多个相同字符串常量看成一个
9.2.2不可以修改字符串常量
9.2.3ANSI并没有制定出字符串是否可以修改的标准,根据编译器不同,可能最终结果也是不同的
/*
const 修饰的变量
在C语言中,const修饰的局部变量,称为伪常量,不可以初始化数组
*/
// 全局变量
const int a = 10; // 常量区
void test01()
{
//a = 100; // 直接修改失败
int * p = &a;
*p = 100;
printf("%d\n", a); // 间接修改,语法通过,运行失败,原因,受到常量区的保护
}
void test02()
{
const int b = 10; // 存放在栈上
//b = 20; // 直接修改失败
int* p = &b;
*p = 20;
printf("%d\n", b); // 间接修改成功
}
/*
字符串常量
*/
void test03()
{
char* p1 = "hello world";
char* p2 = "hello world";
char* p3 = "hello world";
char* p4 = "hello world";
printf("%d\n", p1); // 5个地址完全相同
printf("%d\n", p2);
printf("%d\n", p3);
printf("%d\n", p4);
printf("%d\n", "hello world");
}
void test04()
{
char* str = "hello world";
str[0] = 'x'; // 这里会报错,字符串常量无法修改 有些可以修改,因为用的标准不一样
// 尽量不要修改字符串常量
}