C Programming begining

宏可以带参数 在C++ 中可用inline替代

C Programming benginng

#### 预编译与有参宏
```C++
#define cube(x) ((x)(x)(x))

预编译指令用途

#           空指令,无任何效果
#include    包含一个源代码文件
#define     定义宏
#undef      取消已定义的宏
#if         如果给定条件为真,则编译下面代码
#ifdef      如果宏已经定义,则编译下面代码
#ifndef     如果宏没有定义,则编译下面代码
#elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
#endif      结束一个#if……#else条件编译块

结构体、共用体

结构体位域

struct node
{
    char ch : 4 // 指定占用 4字节
    char ch1 : 4
    int num;
};
typedef struct node node;
node a;
sizeof(a) = 4+4+4 = 12;

结构体空洞

内存对齐机制,占用空间大的成员前的较小的成员由于内存对齐原因同样会占用与占用内存较大成员的内存,在这块内存中没有存有其他成员进行内存补齐,这一现象称为结构体空洞.

解决办法,后根预留成员函数做内存补齐,或者书写时严格按照内存占用从小到大的顺序编写.

如下面的例子:

struct node
{
    char ch;
    int num;
    char ch1;
};

正常分析输出结果应该为1+4+1=6;可输出结果为12;因为第一个char系统为它分配4个字节,可它只用得到一个字节,剩余3个闲置。

第二个是int型,占四个字节,前面只剩3个字节,它不会填充到前面空间中,系统会重新分配给它四个字节,这四个字节被填满了。

然后到第二个char,它不会填充到第一个char型剩余的3个字节中,而是系统又为它分配4个字节存储空间,还是会有3个字节闲置。最后总共占用12个字节。


字符串操作

strchar strlen strcpy strcat strcmp strstr atoi atof strtol

strchar

char* strchar(const char* string, int target) 在字符串中找target(中间会将(int)target转换为(char)target)并返回该target在该串位置的指针.若没有匹配到则返回NULL.

strstr

char* strstr(const char* str, const char* substr) 在str总找到substr子串,若找到则返回该子串第一次出现位置的首字母的指针,找不到返回NULL.

strtol

long strtol( const char *str, char **str_end, int base/10:10进制 8:8进制 16:16进制/);


strlen实现

// v1
size_t MyStrlen(const char* str)
{
	int i = 0;
	while(*str != '\0')
	{
		*str++;
		i++;
	}
	return i;
}

// v2 不用局部变量 全局变量 实现strlen
//Tip: 懂v1 迭代写法就懂 v2 递归写法.
size_t MyStrlen_v2(const char* str)
{
	return *str == '\0' ? 0 : MyStrlen((++str)) + 1;
}

strcpy实现

Tip: 标准库 string.h 提供的 strcpy(char* dest, const char* src) 有潜在的栈溢出风险,当dest 指向内存块大小不足以容纳,src时,字符串拷贝会出现栈溢出.

	char str[2] = "a";
	const char *str1 = "def";
	strcpy(str, str1);
//v1 自动避免栈溢出 dest指向的内存块能拷就拷多少.
void MyStrcpy(char* dest, const char* src)
{
	if (!dest || !src) return;
	while ((*dest != '\0') && (*src != '\0'))
	{
		*(dest++) = *(src++);
	}
}
//v2 自动避免栈溢出 但用户可以指定拷贝长度,在指定的长度小于等于 dest指向的内存块的大小 时成立.
void MyStrcpy(char* dest, const char* src, size_t n)
{
	if (!dest || !src) return;
	int i = 1;
	while ((*dest != '\0') && (*src != '\0')&&i<=n)
	{
		*(dest++) = *(src++);
		i++;
	}
}

// v3 处理内存重叠
void MyStrcpy(char* dest, const char* src, size_t n)
{
	if (!dest || !src) return;
	int i = 0;
	if(dest < src)
	{
		while ((*dest != '\0') && (*src != '\0')&&i<n)
		{
			*(dest++) = *(src++);
			i++;
		}
	}
	else
	{
		dest += (n-1);
		src += (n-1);
		while((*dest != '\0') && (*src != '\0')&&i<n)
		{
			*(dest--) = *(src--);
			i++;
		}
	}
}

memcpy实现

// string.h中的实现,不处理内存重叠,若有内存重叠情况交由memmove()处理;
void* Memcpy(void* dest, const void* src,size_t count)
{
	if (!dest || !src || !count) return NULL;
	char* cdest = (char*)dest;
	const char* csrc = (char*)src;
	int i = 1;
	while ((cdest != '\0') && (csrc != '\0') && i <= count)
	{
		*(cdest++) = *(csrc++);
		i++;
	}
	return dest;
}


// 处理内存重叠
void* Mymemcpy(void* dest, const void* src, size_t count)
{
	if (!dest || !src || !count) return NULL;
	char* cdest = (char*)dest;
	char* csrc = (char*)src;

	int i = 0;
	if (dest < src) //高位地址向地位地址拷贝
	{
		while ((cdest != '\0') && (csrc != '\0') && i < count)
		{
			*(cdest++) = *(csrc++);
			i++;
		}
	}
	else
	{
		cdest += (count - 1);
		csrc += (count - 1);
		while (i < count)
		{
			*(cdest--) = *(csrc--);
			i++;
		}
	}
	return dest;
}

atoi实现

#include <cctype>

int isspace(int ch);

int isdigit(int ch);

// #include <cctype>
//int isspace(int ch); 如果字符是空白字符,返回非零值,否则返回零。
//int isdigit(int ch); 若字符为数字则为非零值,否则为零。
int MyAtol(const char* str)
{
	if (!str)
	{
		std::cout << "str is null!" << std::endl;
		return 0;
	}

	while (isspace(*str)) str++;

	int Inter =	1;
	if (*(str++) == '-')
		Inter = -1;

	int val = 0;
	while (isdigit(*str))
	{
		val = val * 10 + (*str - '0');
		str++;
	}

	return Inter * val;
}

Sizeof 运算符

usage:
sizeof unary-expression
sizeof ( type-name )
  • 传入参数为非数组首地址计算传入参数所占字节大小

  • 传入参数为数组首地址则计算整个地址大小

    Tip: sizeof 运算通常用来计算未知长度数组的长度;
    sizeof array / sizeof array[0]
    
  • 参考连接 msvc - sizeof 运算符 | Microsoft Learn