04-goto.void.extern.sizeof

来源:互联网 发布:火星时代 知乎 编辑:程序博客网 时间:2024/05/27 20:31
遭人遗弃的goto
高手潜规则:禁用goto,程序质量与goto的出现次数成反比;
一般在内核模块的入口函数才会大量使用goto语句,用来处理异常;
goto常常会破坏结构化程序的顺序执行;
goto语句也称为无条件跳转语句,一般格式为
    goto 语句标签;
其中语句标签是按标识符规定书写的符号,放在某一语句行的前面,标号后加冒号(:);
语句标签起标示语句的作用,与goto语句配合使用;
C语言中不限制程序中使用标签的次数,但各标签不得重名;goto语句是改变程序流向,转去执行语句标签所标识的语句;
goto语句一般通常与分支语句配合使用,可用来实现条件转移,构成循环,跳出循环体等功能;
但是结构化程序中不建议使用goto语句,以免造成程序混乱,使理解和调试程序都产生困难;
示例
#include <stdio.h>
int main(){
    int n = 0;
    printf("input a string\n");

loop:
    if (getchar() != '\n') {
        n++;
        goto loop;
    }

    printf("%d\n", n);

    return 0;
}
例如输入: hello world
回车打印: 11

goto副作用分析

goto有可能会造成跳过一些本来应该执行的语句,破坏结构化程序设计顺序执行的规则;
/* goto副作用分析 */
#include <stdio.h>
void func(int n) {
    int* p = NULL;
    if(n < 0) { //当n>=0时程序就会执行的很好
        goto STATUS; //跳过堆内存的分配,使程序崩溃
    }
    /* 破坏结构化程序的顺序执行 */
    p = malloc(sizeof(int) * n);
STATUS:
    p[0] = n;    
}
int main() {  
    f(1);
    f(-1);
    return 0;
}

编译的时候被编译了,但是执行的时候被goto跳过,造成结果的不确定性;
#include <stdio.h>
int x = 5;
int main () {
    printf("%p\n", &x);
    goto a;
    {
        /* 执行的时候goto会跳过,但仍然会被正常编译 */
        int x = 3; /* 又重新申请了一个同名局部变量x */
        printf("%p\n", &x);
        pritnf("int x = 3\n");
a:
        /* 这里的x都是局部变量 */
        printf("x = %d\n", x);
        printf("%p\n", &x);
    }
    /* 以后的x是全局变量 */
    printf("x = %d\n", x);
    printf("%p\n", &x);
    return 0;
}

void关键字
void修饰函数返回值和参数
    如果函数没有返回值,那么应该将其声明为void类型;
    如果函数不接收参数,应该声明其参数为void类型;
    void修饰函数返回值和参数仅为了表示无;
不存在void变量
void v; /* 编译报错 */
没有void的标尺;
    c语言中类型名是固定大小内存的别名,但没有定义void究竟是多大内存的别名;
    void不对应任何变量的大小

void类型的指针是存在的;
void指针的意义
    c语言规定只有相同类型的指针才可以相互赋值;
    void*指针作为左值用于接收任意类型的指针;
    void*指针作为右值赋值给其他指针时需要强制类型转换,才能够赋值给其他类型的指针;
    malloc()返回void*类型;
int *pI = (int *)malloc(sizeof(int));
char *pC = (char *)malloc(sizeof(char));
void *p = NULL;
int *pni = NULL;
char *pnc = NULL;

p = pI; //ok
pni = p; //oops!
p = pC; //ok
pnc = p; //oops!

void*指针的使用,实现my_memset()函数;

#include <stdio.h>
void* my_memset(void *p, char c, int size) {
    void *ret = p;
    char *dest = (char *)p;
    int i = 0;
    for (i = 0; i < size; i++) {
        dest[i] = c;
    }
    return ret;
}

int main(){
    int arr[5] = {1, 2, 3, 4, 5};
    long num = 9999;
    char str[10] = "hello";
    int i = 0;
    for (i = 0; i < 5; i++) {
        printf("%d\t", arr[i]);
    }
    printf("%ld\t", num);
    printf("%s", str);
    printf("\n");

    my_memset(arr, 0, sizeof(arr));
    my_memset(&num, 0, sizeof(num));
    my_memset(str, 65, sizeof(str) - 1);

    for (i = 0; i < 5; i++) {
        printf("%d\t", arr[i]);
    }
    printf("%ld\t", num);
    printf("%s", str);
    printf("\n");

    return 0;
}

extern关键字
extern用于声明外部定义的变量或函数;
extern用于告诉编译器用c方式编译;
    C++编译器和一些变种C编译器默认会按自己的方式编译函数和变量,通过extern关键字可以命令编译器以标准c方式进行编译;
// g++ test.c
#include <stdio.h>
extern "C" {
    int add(int a, int b){
        return a + b;
    }
}
int main(){
    printf("res = %d\n", add(2, 3));
    return 0;
}

extern用于声明外部定义的变量或函数;

// test2.c
int g = 100;
int get_min(int a, int b) {
    return (a < b) ? a : b;
}

//gcc test1.c test2.c
#include <stdio.h>
extern int g; //声明引用外部定义的变量
extern int get_min(int a, int b); // 声明引用外部定义的函数
int main() {  
    printf("g = %d\n", g);
    printf("get_min(3, 5) res %d\n", get_min(3, 5));

    return 0;
}

sizeof关键字
sizeof是编译器的内置指示符,不是函数
sizeof用于计算相应实体所占的内存大小,不用运行就可以知道,编译时确定;
sizeof的值在编译期就已经确定
sizeof不是函数

#include <stdio.h>
int main() {  
    int a;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof a); //sizeof不是函数
    printf("%d\n", sizeof(int));
    /* printf("%d\n", sizeof int );  */
    ////C语言中int前面不能出现unsigned/signed/const之外的;
    ////不能是sizeof
    ////类型是不能这么写的;
    
    return 0;
}


0 0