C语言和LinuxC的一些问题(进程线程)

1. 阅读下面代码段,回答问题

  1. 以下代码段存在问题, 请在不修改结构体定义,不减少功能的前提下对此对此代码中存在的问题进行修改并说明修改原因。
  2. 修改正确后,写出输出结果并说明原因
  3. 将注释加入程序, 能正确运行吗, 如果能, 输出结果是什么, 并说明原因
  4. sizeof(A)的结果是什么,为什么,再定义一个 int 类型的 t3, 定义的位置影响结果吗,说明理由。
typedef struct tag_unimportant {
	char *t2;
	int t1;
} A;
void func(A *a)
{
	a = (A *)malloc(sizeof(A));
	a->t1 = 0x20200011;
	a->t2 = (char* )&(a->t1);
	*(a->t2) = 0x00;
	//strcpy(a->t2, "xiyoulinux");
}
int main(int argc, char *argv[])
{
	A *a;
	func(a);
	printf("%x\n", a->t1);
	//printf(“%s\n”, a->t2);
	return 0;
}

(1)

typedef struct tag_unimportant {
    char *t2;
    int t1;
} A;
void func(A *a)
{
    a->t1 = 0x20200011;
    a->t2 = (char* )&(a->t1);
    *(a->t2) = 0x00;
//strcpy(a->t2, "xiyoulinux");
}
int main(int argc, char *argv[])
{
    A *a = (A *)malloc(sizeof(A));
    func(a);
    printf("%x\n", a->t1);
//printf(“%s\n”, a->t2);
    free(a);
    return 0;
}

修改原因: 原来的代码malloc是在func函数内做的, 而在func函数结束后, func函数栈帧里的局部变量a会销毁, 就不在指向那一段堆空间, 而且那一段堆空间也会出现内存泄露,没有释放, 而那一段堆空间没有变量指向, main函数栈帧里的局部变量a没有初始化, 指向一段垃圾地址.

而修改后局部变量a在main函数里初始化, a指向一段堆空间, 在func函数里对那一段堆空间做了修改, 虽然func函数结束, func函数栈帧里的局部变量a被销毁了, 但是在main函数里局部变量a仍然指向那一段堆空间, 可以访问成功.

第二种解法: 也可以改为地址传递.

#include 
#include 
#include 
typedef struct tag_unimportant
{
    char *t2;
    int t1;
} A;
void func(A **a)
{
    *a =(A *)malloc(sizeof(A));
    (*a)->t1 = 0x20200011;
    (*a)->t2 = (char* )&((*a)->t1);
    *((*a)->t2) = 0x00;
    //strcpy((*a)->t2, "xiyoulinux");
}
int main(int argc, char *argv[])
{
    A *a;
    func(&a);
    printf("%x\n", a->t1);
    //printf("%s\n", a->t2);
    return 0;
}

(2)

输出结果:20200000

原因:

a->t1 = 0x20200011;

因为小端序, 所以内存中从低地址到高地址是11 00 20 20

a->t2 = (char* )&(a->t1);

这一段代码把t2指向了t1, t2存储的是11的内存地址

*(a->t2) = 0x00;

根据那一个地址找到那一块存储空间把11改成了00

所以打印时, 结果为0x20200000

(3)不能正常运行, 会发生内存访问越界, 可能造成严重的后果.

输出结果:
6f796978
xiyoulinux

原因:

把原来的00002020改成了xiyo, 所以a->t1是oyix(因为t1只能访问4个字节, 并且内存寻址是从大到小的)

strcpy(a->t2, "xiyoulinux"); 会发生内存访问越界, 可能造成严重的后果.

以%x打印, 打印的是oyix的ASCII的十六进制形式6f796978

a->t2 是xiyoulinux

这里要注意: 字节顺序有“大端模式” 和 “小端模式” 两种

  • 字节顺序只是对内置数据类型而言,例如对于整型.

  • 但对于字符串没有大端和小端的说法(虽然字符是内置数据类型,但字符串不是),比如char *a = "helloworld",不管系统是大端还是小端模式,其在内存中的字节顺序都是:

    低地址 -------------------------------> 高地址
    ‘h’ ‘e’ ‘l’ ‘l’ ‘o’ ‘w’ ‘o’ ‘r’ ‘l’ ‘d’ ‘\0’

(4)

结果是: 16个字节

因为: 在64位系统中, 指针占用8个字节, int占用4个字节, 因为内存对齐, 所以总内存必须是结构体成员变量中最大数据类型的整数倍, 也就是8的整数倍.并且int因为要地址对齐, 所以在int与指针之间空了4个字节.

影响结果, 因为要考虑内存对齐, 提升CPU对内存的访问效率.

2.分析代码并说明:

a. 最终输出多少次"hello, world?"
b. printf 加上换行符,最终输出多少次"hello, world?";
c. 最终将产生共多少个进程。

int main(int argc, char *argv[])
{
    for (int i = 0; i < 2; i++)
    {
        pid_t pid = fork();
        printf("hello, world?");
    }
    return 0;
}

a : 8

hello, world在缓冲区里, 子进程会拷贝fork之前父进程缓冲区的内容。

C语言和LinuxC的一些问题(进程线程)_第1张图片

b : 6

因为有\n, 所有helloworld会被显示, 不在缓冲区中

C语言和LinuxC的一些问题(进程线程)_第2张图片

c : 4

3.有两个线程在并发执行以下代码段, 其中 g 是 int 类型的全局变量。请问当两个线程都执行完毕该代码段后, g 的值的取值范围为___________。

for (int i = 1; i <= 50000; i++) {
	g += 1;
}

因为将一个变量加1转成汇编为3条指令
从内存读变量值到寄存器
寄存器的值加1
将寄存器的值写回内存
两个线程在多处理器平台上同时执行这三条指令

C语言和LinuxC的一些问题(进程线程)_第3张图片

第二个线程的mov可能会把第一个线程的add覆盖掉, 所以会有冲突.导致加2次只加了1次, 所以取值范围为50000~100000

4.编写一个至少具有三个线程的程序(称之为线程 A、B 和 C),其中线程 A 输出字符’A’,

线程 B 输出字符’B’,线程 C 输出字符’C’。使得最终输出结果为“ABCABCABC…”。

// 条件变量+互斥锁
#include 
#include 
#include 
#include 

pthread_cond_t printed[3];
pthread_mutex_t mutex[3];

void *func1(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex[0]);
        pthread_cond_wait(&printed[0], &mutex[0]);
        putchar('A');
        pthread_mutex_unlock(&mutex[0]);
        pthread_cond_signal(&printed[1]);
    }
}

void *func2(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex[1]);
        pthread_cond_wait(&printed[1], &mutex[1]);
        putchar('B');
        pthread_mutex_unlock(&mutex[1]);
        pthread_cond_signal(&printed[2]);
    }
}

void *func3(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex[2]);
        pthread_cond_wait(&printed[2], &mutex[2]);
        putchar('C');
        pthread_mutex_unlock(&mutex[2]);
        pthread_cond_signal(&printed[0]);
    }
}

int main() {
    pthread_t tid[3];

    for (int i = 0; i < 3; ++i) {
        pthread_cond_init(&printed[i], NULL);
        pthread_mutex_init(&mutex[i], NULL);
    }

    pthread_create(&tid[0], NULL, func1, NULL);
    pthread_create(&tid[1], NULL, func2, NULL);
    pthread_create(&tid[2], NULL, func3, NULL);

    pthread_cond_signal(&printed[0]);

    for (int j = 0; j < 3; ++j) {
        pthread_join(tid[j], NULL);
    }

    for (int k = 0; k < 3; ++k) {
        pthread_mutex_destroy(&mutex[k]);
        pthread_cond_destroy(&printed[k]);
    }

    return 0;
}
// 信号量解法
#include
#include 
#include
#include
#include

sem_t sem[3];

void *func1(void *arg) {
    while (1) {
        sem_wait(&sem[0]);
        printf("A");
        sem_post(&sem[1]);
    }
}

void *func2(void *arg) {
    while (1) {
        sem_wait(&sem[1]);
        printf("B");
        sem_post(&sem[2]);
    }
}

void *func3(void *arg) {
    while (1) {
        sem_wait(&sem[2]);
        printf("C");
        sem_post(&sem[0]);
    }
}

int main() {
    pthread_t tid[3];

    sem_init(&sem[0], 0, 1);
    sem_init(&sem[1], 0, 0);
    sem_init(&sem[2], 0, 0);

    pthread_create(&tid[0], NULL, func1, NULL);
    pthread_create(&tid[1], NULL, func2, NULL);
    pthread_create(&tid[2], NULL, func3, NULL);

    for (int i = 0; i < 3; ++i) {
        pthread_join(tid[i], NULL);
    }

    for (int j = 0; j < 3; ++j) {
        sem_destroy(&sem[j]);
    }

    return 0;
}

你可能感兴趣的:(Linux系统编程)