结构体枚举共同体指针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;
结构体对齐规则(重点)
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小(字节)的较小值。VS中默认的值为8
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐(即对齐数)到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
对齐数原因
- 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
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的主要内容,如果未能解决你的问题,请参考以下文章
入职培训笔记记录--day9(1指针函数与函数指针函数指针数组 2malloc memset 3递归函数 4结构体 5共用体---》大小端 6枚举)