linux C语言

C语言

  • 前言
  • 1. 指针
    • 1.1 题
    • 1.2 数组地址
  • 2. 结构体
    • 对齐
    • 位域
  • 3. 联合体union
  • 4. 内存
  • 5 程序结构
  • 6 堆栈
  • 7 其他
    • sizeof作用于编译阶段
    • const只在编译阶段判断,运行阶段无作用
    • 可变参数宏定义
    • __attribute __
      • 对齐
      • 把函数指定到具体某个section
    • 内联函数inline
  • Linux
    • 线程函数
    • 进程间通信
    • glib

前言

本系列文章介绍c语言的疑难点
linux C语言_第1张图片

1. 指针

1.1 题

来自 https://blog.csdn.net/qq_64484137/article/details/125605822
题1
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( “%d,%d”, *(a + 1), *(ptr - 1));
return 0;
}

题2
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf(“%p\n”, p + 0x1);
printf(“%p\n”, (unsigned long)p + 0x1);
printf(“%p\n”, (unsigned int
)p + 0x1);
return 0;
}

题3
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( “%x,%x”, ptr1[-1], *ptr2);
return 0;
}

题4
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( “%d”, p[0]);
return 0;
}
题5
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( “%p,%d\n”, &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
题6
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int )((aa + 1));
printf( “%d,%d”, (ptr1 - 1), (ptr2 - 1));
return 0;
}
题7
#include
int main()
{
char a[] = {“work”,“at”,“alibaba”};
charpa = a;
pa++;
printf(“%s\n”, *pa);
return 0;
}
题8
int main()
{
char *c[] = {“ENTER”,“NEW”,“POINT”,“FIRST”};
char
cp[] = {c+3,c+2,c+1,c};
char
cpp = cp;
printf(“%s\n”, **++cpp);
printf(“%s\n”, ++cpp+3);
printf(“%s\n”, *cpp[-2]+3);
printf(“%s\n”, cpp[-1][-1]+1);
return 0;
}

题9
常指针与常量的指针

char * const p;

char const * p

const char *p

上述三个有什么区别?

char * const p; //p为只读指针。

char const * p;//p值只读的指针。

const char *p; //和char const *p

1.2 数组地址

代码:

// 数组地址
void test(void)
{
    char a[2][100]={0};
    char b[100]={0};
    printf("%u %u %u %u %u %u %u %u\n",a,a+1,&a+1,a[0],a[1],&a[0]+1,a[0]+1,a[10]);
    printf("%u %u %u %u %u %u\n",b,b+1,&b+1,&b[0],&b[1],&b[0]+1);
}

执行结果:

a          a+1        &a+1       a[0]       a[1]       &a[0]+1    a[0]+1    a[10]
3215204272 3215204372 3215204472 3215204272 3215204372 3215204372 3215204273 3215205272
b          b+1        &b+1       &b[0]      &b[1]      &b[0]+1
3215204472 3215204473 3215204572 3215204472 3215204473 3215204473

可以看出多维数组的地址 a+1,&a+1,&a[0]+1有区别,a+1和&a[0]+1增加100,&a+1增加200
一维数组b+1,&b+1有区别,b+1增加1,&b+1增加100

##1.3 结构体地址

void test(void)
{
    struct ST
	{
		char a[5];
		char b[5];
	};
	
    struct ST testSt[2];
    printf("%u %u %u\n",&testSt,&testSt[0]+1,&testSt+1);
 
}

执行结果:

&testSt    &testSt[0]+1 &testSt+1
3218817404 3218817414 3218817424

可以看出结构体的地址 &testSt[0]+1,&testSt+1有差别

数组和结构体的名称加了取地址符&,则代表整个结构
带上[],则代表相应的维度

2. 结构体

对齐

位域

struct {
      unsigned char x1 : 2;
      unsigned char x2 : 2;
      unsigned char x3 : 2;
      unsigned char x4 : 2;
} test;

该结构体大小为1字节

struct {
      unsigned int x1 : 2;
      unsigned int x2 : 14;
      unsigned int x3 : 7;
      unsigned int x4 : 9;
} test;

该结构体大小为4字节

3. 联合体union

4. 内存

malloc
calloc
calloc = malloc + memset
realloc
malloc(0)
常见的动态内存错误
对NULL指针的解引用操作
对动态开辟空间的越界访问
对非动态开辟使用free释放
使用free释放一块动态开辟内存的一部分
对同一块动态内存多次释放
动态开辟内存忘记释放(内存泄漏)

原文链接:https://blog.csdn.net/weixin_66672501/article/details/125786470
请问以下题目中运行Test 函数会有什么样的结果:

void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, “hello world”);
printf(str);
}
int main()
{
Test();
return 0;
}

首先调用Test函数,创建一个指针变量str并赋值为NULL,然后调用函数GetMemory,参数为str,那么形参也要创建也一个指针变量p来接收,p也是一个空指针,然后用malloc函数来开辟一块100个字节的空间,并把这块空间的起始地址交给p来维护,因为形参只能在本函数内部使用,出了函数形参p销毁,但是malloc开辟的空间并不会销毁。
该函数调用完毕后回到Test,因为是传值调用,并没有传str的地址,所以操作形参并不会改变实参,此时的str还是一个空指针,下面紧接着对空指针str进行字符串拷贝程序会崩溃,因为空指针没办法解引用操作,所以最后printf会打印空,至此程序结束,而GetMemory函数内部用malloc开辟的空间也找不到了,所以也就永远没办法进行释放,这就导致了内存泄漏。

简单来说上述代码有两个问题:1. 解引用空指针崩溃,存在内存泄漏。

修改:GetMemory取出str的地址传参,形参用二级指针接收,然后解引用一次找到str,用str来维护malloc开辟的空间,最后使用完毕释放str,置空。

-----------------分割线------------------

char* GetMemory(void)
{
char p[] = “hello world”;
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}

首先调用Test函数,创建指针变量str并赋值为NULL,然后然后调用GetMemory函数,该函数内部创建数组p,然后返回p也就是数组首元素地址,出了函数数组p的空间就销毁了,此时把这块空间的地址赋值给str时,str的所指向的空间的内容是未知的,因为销毁后空间还给了操作系统,所以里面的内容如果被其它数据使用覆盖了,那么内容是未知的,也有可能没覆盖。所以如果没有被覆盖,可以打印出hello world,否者打印乱码。

因此该程序的问题是返回了局部变量的地址,出了作用域销毁后返回了一个野指针。
-----------------分割线------------------

void GetMemory(char** p, int num)
{
p = (char)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, “hello”);
printf(str);
}
int main()
{
Test();
return 0;
}

这题和上面的第一题相似,只有一个问题就是使用完后没有free掉这块空间,这就导致函数调用完毕后再也无法找到str所指向的那块空间了,也就相当于内存泄漏。

修改:free(str); str = NULL;

-----------------分割线------------------

void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, “hello”);
free(str);
if (str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
int main()
{
Test();
return 0;
}

当free掉这块空间后,此时str依然指向那块内存空间的起始地址,但也是一个野指针,str != NULL为真进入if语句后,对野指针指向的地址进行字符串拷贝,因为那块空间已经不属于我们了,会造成非法访问,所以程序会崩溃。

也是一个野指针问题。

修改:在free后要及时把该指针置空即可。

5 程序结构

代码段、RO Data、RW Data及BSS这四个段都属于程序中的静态区域,堆和栈属于程序的动态区域;
C程序经过编译连接生产的二进制可执行代码,只包含代码段、RO Data、RW Data,在程序运行后才会包含后面两个段,BSS中的数据将会在运行前置0;

6 堆栈

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

7 其他

sizeof作用于编译阶段

const只在编译阶段判断,运行阶段无作用

可变参数宏定义

#define PRINT(format,...)  printf(format, ##__VA_ARGS__)

__attribute __

对齐

struct S 
{
  int a;
  short b[3];
  char c;
} __attribute__ ((aligned (4)));

把函数指定到具体某个section

内联函数inline

Linux

线程函数

线程创建
pthread_t tid;
线程的属性设置
struct sched_param param;
/* 线程属性变量的初始化 /
pthread_attr_init(&attr);
|分离
|调度策略
/
设置线程是否继承创建者的调度策略 PTHREAD_EXPLICIT_SCHED:不继承才能设置线程的调度策略*/
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
/* 设置线程的调度策略:SCHED_FIFO:抢占性调度; SCHED_RR:轮寻式调度;SCHED_OTHER:非实时线程调度策略*/
pthread_attr_setschedpolicy(&attr, SCHED_RR);
|优先级
拥有1-99个静态优先级,数字越大,优先级越高
param.sched_priority = 1;
线程退出
几种退出方式的区别:return,pthread_exit ,pthread_cancel,
return pthread_exit:作用相同

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。//线程return
  2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。//被动终止
  3. 线程可以调用pthread_exit终止自己。//自己终止

线程资源回收
Linux系统中程序的线程资源是有限的,表现为对于一个程序其能同时运行的线程数是有限的。而默认的条件下,一个线程结束后,其对应的资源不会被释放,于是,如果在一个程序中,反复建立线程,而线程又默认的退出,则最终线程资源耗尽,进程将不再能建立新的线程。
解决这个问题,有2种方式,系统自动释放线程资源(分离),或者由另一个线程释放该线程资源(pthread_join)。

线程分离的两种方式
创建线程时设置分离属性
pthread_attr_t attr 创建一个线程属性结构体
pthread_attr_init(&attr); 初始化线程属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);// 设置线程属性为分离态
pthread_create(&tid,&attr,fun,NULL);
pthread_attr_destroy() //销毁线程属性
线程外或线程内调用pthread_detach();
线程内采用pthread_detach(pthread_self());
线程外采用pthread_detach(thread_id);

互斥量

进程
进程调度

进程间通信

glib

你可能感兴趣的:(c语言,linux,开发语言)