1.宏函数的使用方式
使用宏函数在一定程度上比普通函数效率高,普通函数会有入栈和出栈上的时间花费。
通常将比较频繁短小的函数写为宏函数
它的优点是以时间换空间
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define MYADD(x,y) ((x)+(y))
//宏函数要保证运算的完整性 添加括号
//宏函数在一定程度上会比普通函数效率高,而普通函数会有入栈和出栈的实际
//通常会将一些使用频繁、短小的函数封装为宏函数
//优点:以空间换时间
int myadd(int x, int y)
{
return x + y;
}
static void test01()
{
int a = 10;
int b = 20;
int ret = MYADD(a, b)*20;//((a) + (b))*20
printf("%d\n", ret);
}
int main01()
{
test01();
return 0;
}
2..变量的传递作用域
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
int g_a = 122;//全局区数据,在main、func1、func2中都可使用
static void func2()
{
}
static void func1()
{
int a = 10;//在main函数中不可使用,在func1和func2中都可使用
func2();
}
int main02()
{
int a = 10;//在main和func1、func2中都可使用
func1(&a);
return 0;
}
3.栈的生长方向和内存的存储方式
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//1、栈 生长方向
static void test01()
{
int a = 10;//栈底 高地址
int b = 20;
int c = 30;
int d = 40;//栈顶 低地址
printf("%d\n", &a);
printf("%d\n", &b);
printf("%d\n", &c);
printf("%d\n", &d);
}
//2、内存存储方式
static void test02()
{
int a = 0xaabbccdd;
unsigned char* p = &a;
printf("%x", *p);//dd 低位字节数据放在低地址
printf("%x", *(p+1));//cc 高位字节数据放在高地址
}
int main03()
{
//test01();
test02();
return 0;
}
4.空指针和野指针
空指针:允许向NULL和非法地址拷贝内存
野指针:未初始化指针
malloc后free,但是没有将指针置空
指针操作超越变量作用域
需要注意的是:
空指针可以释放,但野指针不可以释放
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//1、空指针 不允许向NULL和非法地址拷贝内存
static void test01()
{
char* p = NULL;
//给p指向的内存区域拷贝内容
strcpy(p, "1111");//err
char* q = 0x1122;
//给q指向的内存区域拷贝内容
strcpy(q, "2222");//err
}
int* dowork()
{
int a = 10;
int* p = &a;
return p;
}
//2、野指针
static void test02()
{
int* p;//未初始化指针
printf("%d\n", *p);
//malloc后free,但是指针没有置空
int* q = malloc(sizeof(int));
*q = 100;
free(q);//野指针 未置空
q = NULL;//将free后的指针要置空,防止野指针的出现
printf("%d\n", *q);
//指针操作超越变量作用域
int* p2 = dowork();//p2是野指针
printf("%d\n", *p2);
printf("%d\n", *p2);
}
static void test03()
{
int* p = NULL;
free(p);//空指针可以释放不止一次
free(p);
free(p);
int* p2 = malloc(4);
free(p);
free(p);//此处指针释放后没有置空,是野指针,野指针不可以释放
}
int main04()
{
//test01();
// test02();
test03();
return 0;
}
5..指针的步长
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<stddef.h>//offsetof的头文件
//指针的步长
//指针变量+1之后 跳跃的字节数
static void test01()
{
char* p = NULL;
printf("%d\n", p);
printf("%d\n", p+1);
double* p2 = NULL;
printf("%d\n", p2);
printf("%d\n", p2 + 1);
}
//2、解引用的时候,取的字节数
static void test02()
{
char buf[1024] = { 0 };
int a = 1000;
memcpy(buf+1, &a, sizeof(int));
char* p = buf;//通过p找到buf首地址
printf("buf中的a=%d\n", *(int*)(p+1));
}
//3、自定义数据类型
struct person
{
char a;//0~3
int b;//4~7
char buf[64];//8~71
int d;//72~75
};
static void test03()
{
struct person p1 = { 'a',10,"hello world",1000 };
//计算结构体中属性的偏移
printf("p1.d的偏移量为:%d", offsetof(struct person, d));//计算结构体中成员d的偏移量
//打印d的值
printf("p1.d的值为:%d\n", *(int*)((char*)(&p1) + offsetof(struct person, d)));
}
int main05()
{
//test01();
//test02();
test03();
return 0;
}
6.指针的间接赋值
条件:一个普通变量和一个指针变量(一个实参或一个形参)
建立关系
通过*进行赋值
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
static void changevalue(int* p)//a2实参 p形参 int*p=&a2
{
*p = 1000;
}
//1、一个普通变量和一个指针变量(或者实参和形参)
//2、建立关系
//3、通过*进行赋值
static void test01()
{
int a = 10;
int* p = NULL;
p = &a;
*p = 100;
printf("%d\n", a);
int a2 = 10;
changevalue(&a2);
printf("%d\n", a2);
printf("%d\n", &a2);
}
int main06()
{
test01();
return 0;
}
7.指针作为函数参数
输入特性:在主调函数中分配内存,被调函数使用
输出特性:在被调函数中分配内存,主调函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//1、输入特性:主调函数中分配内存,被调函数中使用内存
static void func(char* p)
{
strcpy(p, "hello world");
}
static void test01()
{
//分配到栈上
char buf[1024] = { 0 };
func(buf);
printf("%s\n", buf);
}
static void printstring(char* str)
{
printf("%s\b", str);
}
static void test02()
{
//分配到堆区
char* p = malloc(sizeof(char) * 64);
memset(p, 0, 64);
strcpy(p, "hello world");
printstring(p);
}
//2、输出特性:被调函数分配内存,主调函数使用内存
static void allocatespace(char** pp)
{
char* temp = malloc(sizeof(char) * 64);
memset(temp, 0, 64);
strcpy(temp, "hello world");
*pp = temp;
}
static void test03()
{
char* p = NULL;
allocatespace(&p);
printf("%s\n", p);
}
int main07()
{
//test01();
//test02();
test03();
return 0;
}
8..字符串的拷贝、逆置和初始化
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
static void test01()
{
//注意字符串需要有结束标志'\0'
char str1[] = { 'h','e','l','l','o','\0' };//不加'\0'末尾有乱码
printf("%s\n", str1);
//初始化部分数组,剩余填写0
char str2[100] = { 'h','e','l','l','o' };
printf("%s\n", str2);
//如果以字符串初始化,那么编译器默认会在字符串尾部添加'\0';
char str3[] = "hello";
printf("%s\n", str3);
printf("sizeof str:%d\n", sizeof(str3));//sizeof统计\0
printf("strlen str:%d\n", strlen(str3));//strlen不统计\0
char str4[100] = "hello";
printf("sizeof str:%d\n", sizeof(str4));//100
printf("strlen str:%d\n", strlen(str4));//5
char str5[] = "hello\0world";
printf("%s\n", str5);//hello
printf("sizeof str5:%d\n", sizeof(str5));//12
printf("strlen str5:%d\n", strlen(str5));//5 统计到\0结束
//8进制表示方式是在数字前加0
char str6[] = "hello\012world";// \012是8进制下的\10 \10在ASCII中是换行的意思
printf("%s\n", str6);
printf("sizeof str6:%d\n", sizeof(str6));//12
printf("strlen str6:%d\n", strlen(str6));//11
}
//字符串拷贝
//参数1__目标字符串 参数2__源字符串
//将源字符串内容拷贝到源字符串
//方式1 利用[]方式拷贝
static void copystring01(char* dest, char* source)
{
int len = strlen(source);
for (int i = 0; i < len; i++)
{
dest[i] = source[i];
}
dest[len] = '\0';
}
//2、第二种利用字符串指针进行拷贝
static void copystring02(char*dest,char*source)
{
int len = strlen(source);
while (*source != '\0')
{
*dest = *source;
dest++;
source++;
}
*dest = '\0';
}
static void test02()
{
char* str = "hello world";
char buf[1024];
//copystring01(buf, str);
copystring02(buf,str);
printf("%s\n", buf);
}
//方式3
static void copystring03(char* dest, char* source)
{
while (*dest++ = *source++){}
}
//字符串逆置 方式1
static void reversestring01(char*str)
{
int len = strlen(str);
//起始位置下标
int start = 0;
//结束位置下表
int end = len - 1;
while (start < end)
{
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
//方式2利用指针
static void reversestring02(char* str)
{
int len = strlen(str);
char* start = str;
char* end = str + len - 1;
while (start < end)
{
char* temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
static void test03()
{
char str[] = "abcedf";
//reversestring01(str);
reversestring02(str);
printf("%s\n", str);
}
int main09()
{
//test01();
//test02();
test03();
return 0;
}
9.sprintf格式化字符串
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//sprintf 格式化字符串
void test01()
{
char buf[1024];
memset(buf, 0, 1024);
sprintf(buf, "%d年%d月%d日", 20121, 12, 13);
printf("%s\n", buf);
//字符串拼接
memset(buf, 0, 1024);
char str1[] = "hello";
char str2[] = "world";
int len = sprintf(buf, "%s%s", str1, str2);//sprintf返回值是字符串长度
printf("buf:%s len%d\n", buf, len);
//数组转换为字符串
memset(buf, 0, 1024);
int num = 100;
sprintf(buf, "%d\n", num);
printf("buf:%s\n", buf);
//设置宽度 右对齐
memset(buf, 0, 1024);
sprintf(buf, "%8d", num);
printf("buf:%s\n", buf);
//设置宽度 左对齐
memset(buf, 0, 1024);
sprintf(buf, "%-8d", num);
printf("buf:%s\n", buf);
//转成16进制字符串 小写
memset(buf, 0, 1024);
sprintf(buf, "0x%x", num);
printf("buf:%s\n", buf);
//转成8进制字符串
memset(buf, 0, 1024);
sprintf(buf, "0%o", num);
printf("buf:%s\n", buf);
}
int main()
{
test01();
return 0;
}