结构体枚举共同体指针bing

Posted 鸟随二月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构体枚举共同体指针bing相关的知识,希望对你有一定的参考价值。

上一篇

数组和结构体整体初始化只有一次即定义时。

汉字

GBK编码 汉字占2字节
UTF-8变长编码 符号占 1~4字节 汉字通常通常3个
windows 默认GBK
linux 默认UTF-8

结构体

创建、访问修改

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef struct Student
int id;
char name[10];
 Student;
int main()

Student student=1,"顿";
student.id=2;
strcpy(student.name,"器");
printf("%d,%s",student.id,student.name);
 return 0;
 

匿名结构体

//匿名结构体类型
struct

 int a;
 char b;
 float c; x;
struct

 int a;
 char b;
 float c; a[20], *p;在这里插入代码片

创建的目的,是为了让这个结构体的类型只使用一次,不在后面继续使用了.

结构体自引用

//代码2
struct Node

 int data;
 struct Node* next;
;

typedef struct Node

 int data;
 struct Node* next; Node;

结构体指针访问

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef struct Student
int id;
char name[10];
 Student;
int main()

Student student=1,"顿";
Student* p=&student;
printf("%d,%s",(*p).id,p->name);
 return 0;
 

结构体传参

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
typedef struct Student
int id;
char name[10];
 Student;
//结构体传参
void print1(Student s) 
 printf("%d\\n", s.id);

//结构体地址传参
void print2(Student* ps) 
 printf("%d\\n", ps->id);

int main()

Student student=1,"顿";
Student* p=&student;
print1(student);//传结构体
print2(p); //传地址
 return 0;
 

结构体对齐规则(重点)

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小(字节)的较小值。VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐(即对齐数)到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

对齐数原因

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    2.数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
    总体来说:
    结构体的内存对齐是拿空间来换取时间的做法。

修改默认对齐数

#include <stdio.h>
#pragma pack(8)//默认对齐数修改8
struct S1

 char c1;
 int i;
 char c2;
;
#pragma pack()//取消设置的默认对齐数,还原为默认

数据存储

借助vssudio调试器的内存窗口查看

步骤
1.打断点

2.调试

3.打开内存窗口

字节存储方式

例子



3.


4.


5.

unsigned int i;
for(i = 9; i >= 0; i--) 
    printf("%u\\n",i);

无限速循环,因为为i是无符号的,所以永远是大于等于0
6.


7.

#include <stdio.h>
unsigned char i = 0;
int main()

    for(i = 0;i<=255;i++)
   
        printf("hello world\\n");
   
    return 0; 

无符号char的范围0-255,所以无限循环

浮点数



指针储存


由指针创建的不可修改,修改的话奔溃

sizeof 发生的时间段

const的使用

assert

#include<Windows.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
int main()


	char a[15] =  'a','a','a' ,'a' ,'a' ,'a' ,'a','a','a','a' ;
	assert(strlen(a) != 10);
	printf("%d", strlen(a));
	system("pause");
	return 0;

错误分类

size_t

函数

字符串函数

字符串函数

size_t strlen ( const char * str );
char* strcpy(char * destination, const char * source );
char * strcat ( char * destination, const char * source );
int strcmp ( const char * str1, const char * str2 );
char * strncpy ( char * destination, const char * source, size_t num );
char * strncat ( char * destination, const char * source, size_t num );
int strncmp ( const char * str1, const char * str2, size_t num );
//strncpy和strncat 就是多加了一层限制.
//通过n限制最大拷贝的个数,从而避免越界的情况~
//虽然有了这样的限制,但是对于程序稳定性的改善,仍然没有任何用处.......
//strncpy 和strncat 都是比较鸡肋的设定~

strstr

char * strstr ( const char *str1, const char * str2);
//返回str2在str1中首次出现的位置
//自定义的
// 指针为空和字符串是空字符串, 是完全不一样的!!
// char* s = NULL; // 毛都没有, 内存都没分配的
// char* s = "";  // "" 里面只有一个 \\0
const char* myStrstr(const char* str1, const char* str2) 
	assert(str1 != NULL);
	assert(str2 != NULL);
	assert(*str1 != '\\0');
	assert(*str2 != '\\0');

	const char* black = str1;

	while (*black != '\\0') 
		const char* red = black;
		const char* sub = str2;

		while (*red != '\\0' && *sub != '\\0'
			&& *red == *sub) 
			// 如果 red 和 sub 指向的相同, 就继续往后比较
			red++;
			sub++;
		
		// 如果循环结束, 此时涉及到三种可能
		// if (*sub == '\\0') 
		// 	// 1. *sub == '\\0'
		// 	return black;
		// 
		// else if (*red == '\\0') 
		// 	// 2. *red == '\\0'
		// 	// 说明是当前已经无法匹配到合适的子字符串了. 
		// 	// 就直接 black 再 ++, 随着 black++ 也就接近了 black == '\\0', 这个最终的结束条件
		// 	black++;
		// 
		// else 
		// 	// 3. *red != *sub
		// 	black++;
		// 
		if (*sub == '\\0') 
			return black;
		
		black++;
	
	// 如果整个循环都结束了, 还没有找到匹配的结果. 
	// 此时说明就不存在匹配的子字符串了. 于是直接返回 NULL 即可~~
	return NULL;


计算机存储字符的时候都是存储的该字符的ASCII码值,所以‘\\0’存储在计算机就是0x00,数字0也是0x00,NULL也是0x00。这三个存储的本质是一样的,但是为了更好的区分,应用场景不太相同。字符时用‘\\0’,指针是用NULL,0x00很少见,一般直接用0代替,这三个本身是一个东西。

strtok

#define _CRT_SECURE_NO_WARNINGS
#include<Windows.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
int main()

	char p[10] = "123121212",q[7] = "12";
	char* pc = strtok(p, "1");
	while (pc != NULL) 
		printf("%s\\n", pc);
		pc = strtok(NULL, "1");
	
	system("pause");
	return 0;

字符函数

内存相关函数

memcpy

void * memcpy ( void * destination, const void * source, size_t num );

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 ‘\\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。

//自定义的
void* myMemcpy(void* dest, const void* src, size_t num) 
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);

	// void* 本身, 不能够进行解引用. 
	// 要想进行内存/指针之间的赋值, 就需要把 void* 强转成其他类型的指针. 
	// 由于咱们是按照字节为单位进行拷贝的, 因此就直接使用 char* 就好了~
	char* pdest = (char*)dest;
	char* psrc = (char*)src;
	for (size_t i = 0; i < num; i++) 
		pdest[i] = psrc[i];
	
	return dest;


memmove

void * memmove ( void * destination, const void * source, size_t num );

memmove这个函数,也是进行内存拷贝~和memcpy最大的区别在于,能支持内存重叠的情况~(src和dest的内存重叠了,实际上如今memcpy也可以处理重叠关系了)

从尾部拷贝条件
src<=dest<=src+size;

//自定义
void* myMemmove(void* dest, const void* src, size_t num) 
	assert(dest != NULL);
	assert(src != NULL);
	assert(num != 0);

	char* pdest = (char*)dest;
	char* psrc = (char*)src;
	if (pdest >= psrc && pdest < psrc + num) 
		// 内存重叠了
		// 需要反着拷贝
		// 注意!!! size_t 是无符号的, 不能判定 >= 0
		for (long i = (long)(num - 1); i >= 0; i--) 
			pdest[i] = psrc[i];
		
	 else 
		// 内存不重叠
		// 直接正着拷贝即可
		for (size_t i = 0; i < num; i++) 
			pdest[i] = psrc[i];
		
	

memcmp

int memcmp ( const void * ptr1, const void * ptr2,size_t num );

//自定义
// p1 < p2, 返回 <0 ; p1 > p2, 返回 >0; p1 和 p2 相等, 返回 0
int myMemcmp(const void* p1, const void* p2, size_t num) 
	assert(p1 != NULL);
	assert(p2 != NULL);
	assert(num != 0);

	char* pp1 = (char*)p1;
	char* pp2 = (char*)p2;
	for (size_t i = 0; i < num; i++) 
		if (pp1[i] < pp2[i]) 
			return -1;
		 else if (pp1[i] > pp2[i]) 
			return 1;
		 else 
			// 表示比较下一个字节
			continue;
		
	
	// 表示相等
	return 0;


memset

void * memset ( void * ptr, int value, size_t num );

//自定义
void* myMemset(void* dest, int val, size_t num) 
	assert(dest != NULL);
	assert(num != 0);

	char* pdest = (char*)dest;
	for (size_t i = 0; i < num; i++) 
		// 此处的赋值就可能会涉及到截断. 
		// 一般给 val 设置的值都是一个字节能表示的值. 
		pdest[i] = (char)val;
	
	return dest;

位段

位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

声明


内存布局不固定

虽然标准是规定,带:得是unsigned int或者int
但是实际上编译器实现的时候,把 char , short 啥的给带上∶编译器也能识别~~

枚举型使用

enum Color//颜色

 RED=1,
 GREEN=2,
 BLUE=4

 - [ ] List item

;
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

由于在C中,枚举被当做一个单纯的int.因此枚举类型的内存布局,就和int是一致的~

联合体(共用体)

定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

union Un

 int i;
 char c;
;
union Un un;
// 下面输出的结果是一样的吗?
printf("%d\\n", &(un.i));
printf("%d\\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\\n", un.i);

使用场景:ip的不同表示

使用共同体判断大段小段储存

int isda() 
	union 
		char a;
	

以上是关于结构体枚举共同体指针bing的主要内容,如果未能解决你的问题,请参考以下文章

共用体与枚举

结构体,宏,枚举,函数,指针

C语言-枚举共用体

C语言-枚举共用体

入职培训笔记记录--day9(1指针函数与函数指针函数指针数组 2malloc memset 3递归函数 4结构体 5共用体---》大小端 6枚举)

详解C语言结构体枚举联合体