C语言常见重要结论。

1.数组与*符号在某种情况下等价。

当作为函数的入参的时候,

2.数组变量是指针常量

如下代码,注释掉一行,这一行是不对的,我们知道,字符串是一个存储与字符串常量区的地址。a=“jjj”;表示把这个地址赋值给a ,但是a是一个指针常量,相当于const char *p="hello"后面是不允许我们作修改的。

    char a[]="hello";
    printf("%s\n",a);
//    a="jjj";
    strcpy(a,"jjj");
    printf("%s\n",a);

3.ifndef

这个只是防止头文件重复包含,只针对同一个文件多次包含,不是针对所有文件,所以理论上一个变量在头文件定义会导致重复定义的问题

头文件只建议使用声明

//a.h文件声明
extern int a;

//a.c文件定义
int a=0;

//main.c
include "a.h"

关于全局 变量的问题

//a.c文件中,是正确的,这表示声明而已,而不是定义
int a;
int a;
int a;
//但是下面出现=符号表示定义,就不能有多个了。
int a=9;

malloc 是在堆区中分配内存的

变量是存储与栈区,系统是会自动进行回收的。但是对于 malloc 分配的内存,如果不使用free进行释放,会导致内存泄露

结构体可以通过=进行赋值

struct Student p1={12,"blueboz",88};
struct Strudent p2=p1;
//如果使用指针类型,不能
struct Student *p3;
//不能直接进行赋值,否则会导致段错误
p3->age=19;//此行错误
p3=&p1;//才可以操作

//下面直接操作Student里面的info 会导致段错误。因为指针没有指向,最好使用非指针
struct Info{
    int age;
    char name[50];
};
//1.struct 是关键字
//
struct Student{
    int age;
    char name[50];
    int score;
    struct Info *info;
};//有分号

a[0] 与 *(a+0) 与 *(a)是等价的

const修饰的指针

    struct Student tmp;
    //const 修饰的是* 指针指向的内存无法进行修改
    const struct Student *p2=&tmp;
    struct Student const *p2=&tmp;

    //const 修饰的是p2,指针指向不能修改,但是指针指向的内容是可以修改的
    struct Student * const p2=&tmp;

    //指针指向无法修改,指针指向的内容也无法修改
    const struct Student * const p2=&tmp;

为什么void类型变量不存在而void 类型指针存在

因为对于不同类型的变量,首先我们需要确定的是他占用的空间的大小,但是void类型我们并无法确定,但是对于指针类型却是可以的,因为指针类型占用的空间大小完全由操作系统决定的。对于32位操作系统,指针占用的大小是4字节,对于64位,占用的8字节。

内存四区

堆区,栈区,全局区,代码区。

只要放在大括号里面的变量都是局部变量,只要放在大括号外面的变量都是全局变量。

全局区:全局变量和静态变量,里面分为初始化区和未初始化区。文字常量区:字符常量区。整个程序运行完毕,自动回收。
常量区:字符串常量和其他的常量的存储位置,程序完成之后自动释放。
注意,常量也是放在全局区。
在这里插入图片描述

数据类型别名

//定义u32为unsigned int
typedef unsigned int u32;
//定义结构体的别名
typedef struct People{
    char name[64];
} People;

数据类型本质

数据类型本质是固定内存大小的别名;是个模具,c语言规定:通过 数据类型定义变量

堆栈的生长方向在这里插入图片描述

先定义的变量拥有高的地址,后定义的有低的地址,但是数组是摆放是按照从低到高的方式。在这里插入图片描述
如图 8c > 88 ,但是有意思的是,数据后定义,居然地址是在高位的?跟书上说的不一样,暂且不深究了。

Mac 实现system(“pause”)

system("read -n 1 -s -p \"Press any key to continue...\"");

字符串反转

通过一个头指针,一个尾指针,相互交换内容效率很高。比传统的递归方式效率要高很多。大概是5倍左右。

/**
 * @brief inverse 交换指针指向内容
 * @param org
 * @return
 */
void inverse(char *org){
    //str =012345
    int len=strlen(org);
    char* start=org;
    char* end=org+len-1;
    char tmp=0;
    while(end>start){
        tmp=*end;
        *end=*start;
        *start=tmp;
        end--;
        start++;
    }
}

查看栈大小

➜  build-dm01-Desktop_Qt_5_13_1_clang_64bit-Debug limit  
cputime         unlimited
filesize        unlimited
datasize        unlimited
stacksize       8MB
coredumpsize    0kB
addressspace    unlimited
memorylocked    unlimited
maxproc         2784
descriptors     2560

导航文章
Linux各类limit设置

指针数组的理解

指针本是一种类型,但又说什么类型的指针,只不过是说指针所指向的 数据是什么类型而已。那么指向数组类型的指针,就只好叫数组指针。

int a[10]; //a的类型是⼀一个指向int类型的指针。
&a; //&a的类型是⼀一个指向数组int[10]类型的指针。

在这里插入图片描述

宏定义

要注意,宏定义一般都是直接展开的。遇到if else 语句可能会发生问题。

#define SAFE_DELETE(p)	\
    do{ 				\
        free(p);		\
        p=NULL;			\
    } while(0);

类似如下宏定义就存在着一定的问题。

 #define macro(condition) \
if(condition) dosomething()

如果是直接展开,那么就会有问题。

if(temp)
    macro(i);
else
    doAnotherThing();

else 变成了与macro里面的if 进行结合。这也是为何建议使用do while(0)的原因了。

结构体字节对齐

就算是一样的结构体,但是你定义的位置不一样,导致最终所占用的空间也是不一样的。

struct Sutdent{
    double d;	//8 8*1=1 2 3 4 5 6 7 8
    char c;		//1	1*9=9
    short s; 	//2	2*5=11 12
    int i;		//4 4*3=13 14 15 16
    //占用16字节
};
struct Student{
   char c; 		//1*1=1
   double d;	//8*1=9 10 11 12 13 14 15 16
   short s;		//2*8=17 18
   int i;		//4*5=21 22 23 24
   //占用24 字节
};

在这里插入图片描述
在这里插入图片描述
可以采用如下的命令来执行字节对齐。

字节对齐可以程序控制,采用指令:
#pragma pack(xx)
#pragma pack(1)
#pragma pack(2)
#pragma pack(4)
#pragma pack(8)
#pragma pack(16)
//1字节对⻬齐 //2字节对⻬齐 //4字节对⻬齐 //8字节对⻬齐 //16字节对⻬齐

文件操作

在这里插入图片描述
其实记住上面的表也很简单,其实核心操作就是r w a 即读写追加
b,只是为了块读写操作。。+表示额外赋权,即写有读,读有写

memwatch 使用

首先是将头原件与源代码引入到项目中。
在这里插入图片描述
其次添加宏,可以在qt.pro里面添加,也可以在main.h里面添加。
DEFINES += MEMWATCH

最后是在程序执行地方调用
mwInit();
结束地方调用
mwTerm();
建议看压缩包里面 的USING文档。
在这里插入图片描述

动态库的显式调用

在windows平台调用方法。
https://blog.csdn.net/Hilavergil/article/details/78544424
总结如下

typedef double(SQRTPROC)(double); HINSTANCE hInstance;
SQRTPROC* pFunction;
 VERIFY(hInstance=::LoadLibrary("c:\\winnt\\system32\\mydll.dll"));
VERIFY(pFunction=(SQRTPROC*)::GetProcAddress(hInstance,"SquareRoot"));
double d=(*pFunction)(81.0);//调⽤用该DLL函数

在linux mac平台下的调用方法
https://blog.csdn.net/lc_910927/article/details/42393121

总结几句
1.导入
#include <dlfcn.h>
2.其次,编译的时候导入-ldl库即libdl.dylib (mac)或者 libdl.so(linux)
#LIBS += -ldl

//打开动态库
void *hand = dlopen(fullpath,RTLD_NOW);
//定义函数指针
typedef void (*SAYMYNAME)();
//从handler 里面获得函数地址,并且转化成函数指针
SAYMYNAME p1=(SAYMYNAME)dlsym(hand,"sayMyName");
//执行函数调用
p1();
//关闭
dlclose(hand);
//查错
char err=dlerror();


发布了73 篇原创文章 · 获赞 55 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/blueboz/article/details/100763961