双层for循环

情况一:两层for循环,外层循环次数为1000000,内层循环次数为10。
情况二:两层for循环,内层循环次数为1000000,外层循环次数为10。
正常情况下,次数多的循环放在内层,会减少cache更新数据的次数,当离开内层循环的时候会恢复外层的计数器,外层循环次数越少,恢复计数器的次数越少,效率较高

例外

// 1
for(i=0;i<100000;i++)
  for(j=0;j<10;j++)
    a[i,j]=0;
//2
for(i=0;i<10;i++)
  for(j=0;j<100000;j++)
    a[j,i]=0;

a为i行j列的数组,读取数组时,还要考虑到数组的存储方式,按行读是顺序存储
因为cpu从内存中获取到数据存放到cache的时候,是一块一块获取的,如果cache的利用率高,那么效率肯定更高。因为第一个代码对数组a的访问是顺序的,所以换页次数更少,时间开销更小。

分析>> 1和/2的区别

效率不同

因为>> 是右移符百号,它在操作时只允许整数
/是除法,它可以操作不同类型的数据:浮点数除法最终结果是浮点数,整数除法的最终结果是整数。
右移操作通常情况下,会比整数除法速度快。涉及容到浮点数的除法速度是最慢的。

结果可能不同

n为非负数时,>> 1和/ 2的结果是一样的
n为负数且还是偶数时,>> 1和/ 2的结果是一样的
n为负数且还是奇数时,>> 1和/ 2的结果是不一样的
原因是奇数除二会发生截断现象。而>> 1和/ 2在n为负奇数时截断的反向不一样。
-5 / 2 = -(int)2.5 = -2,这里是把绝对值变小了,加个负号,结果就变大了。
-5 >> 1 = (1011) >> 1 = (1101) = -3,假设用4-bit表示一个整数,补码表示。发现结果变小了。

函数返回局部变量/局部指针

局部变量

局部变量分局部自动变量和局部静态变量,由于c返回的是值,因此返回一个局部变量是可以的,无论自动还是静态,因为这时候返回的是这个局部变量的值。另外,函数返回局部变量时实际上是返回变量值的拷贝。a为局部变量,在栈区存储,虽然在函数调用结束后所在内存会被释放回收掉,但返回值不是访问地址,而是a的拷贝副本。

int INTtest(){
     int a = 10;
     return a;
 }

局部指针

局部指针跟上面所述的局部变量一样。可以返回一个局部指针的值,也可以返回一个局部静态指针的地址,但不应该返回一个局部自动指针的地址,除非自动指针的地址指向数据区或堆区。

返回值为局部指针,可以分为:(1)声明局部变量,返回其地址;(2)声明局部数组,返回数组名;(3)声明局部指针,返回该指针。

1.返回局部变量地址,局部变量分为自动和静态局部变量,不应该返回指向局部自动变量的指针,因为函数调用结束后栈上声明的局部自动变量被抛弃,这个指针指向一个不再存在的对象,是无意义的。但可以返回指向局部静态变量的指针,因为静态变量存在数据区,它的生存期从定义起到程序结束。

2.返回数组名,与局部变量相同(自动和静态),调用的结果指向栈上声明的数组的首地址,函数结束后自动数组内存释放掉,将无法对其进行访问(某些编译器Release下可以访问,但理论上是没有意义的),但静态数组可以。

3.返回指针,调用结果指向该指针指向的内存,在函数结束后在栈上声明的指针也会被释放掉,但应该注意原指针指向的内存地址,若改地址同样是栈上声明的,则无法访问,如果是数据区或堆区的内容,则可以访问。

1和2比较容易理解,这里不再进行举例,仅对第3种情况的数据区和堆区进行说明。

数据区:返回指针,但指针指向的内存地址存储在数据区(常量区)

char * test(){
    char *arr = "hello world"; //字符常量
    //static char arr[] = "hello world!";
    return arr;
}

void  test_(){
    char *s = test();
    printf("s = %s\n", s);
}

int main(){
    test_();
        return 0;
}

堆区:返回指针,指针指向的内存地址存储在堆区,没有手动释放前都可以访问到。
malloc()堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。如果希望在分配内存的同时进行初始化,请使用 calloc() 函数。

char *getstring(){
    char *p;
    p = malloc(100);
    memset(p, 0, 100);
    strcpy(p, "hello");

    return  p;
}

void test(){
    char *ret = getstring();
    printf("%s\n", ret);
    free(ret);
    ret = NULL;
}

int main(){

    test();

    system("pause");
    return 0;
}

如果指针指向的内存是在函数内部申请的,基本不干返回指针这种事,做这种事情很有可能是在给自己找麻烦,多数做的都是在可以释放的地方申请好内存,通过指针传递进函数,申请和释放都在同一函数中。上面的函数,可以修改如下:

void getstring01(char *p){
    strcpy(p, "hello");
}

void test01(){
    char *p = NULL;
    p = malloc(100);
    memset(p, 0, 100);
    //char *ret = NULL;
    getstring01(p);
    printf("%s\n", p);
    free(p);
    p = NULL;
}


int main(){
        test01();
        return 0;
}

const和指针

有时候我们希望定义这样一种变量,它的值不能被改变,在整个作用域中都保持固定。
由于常量一旦被创建后其值就不能再改变,所以常量必须在定义的同时赋值(初始化),后面的任何赋值行为都将引发错误。

#include <stdio.h>

int getNum(){
    return 100;
}

int main(){
    int n = 90;
    const int MaxNum1 = getNum();  //运行时初始化
    const int MaxNum2 = n;  //运行时初始化
    const int MaxNum3 = 80;  //编译时初始化
    printf("%d, %d, %d\n", MaxNum1, MaxNum2, MaxNum3);
    #100, 90, 80
    return 0;
}

const 和指针

const 也可以和指针变量一起使用,这样可以限制指针变量本身,也可以限制指针指向的数据。const 和指针一起使用会有几种不同的顺序,如下所示:

const int *p1;
int const *p2;
int * const p3;

在最后一种情况下,指针是只读的,也就是 p3 本身的值不能被修改;在前面两种情况下,指针所指向的数据是只读的,也就是 p1、p2 本身的值可以修改(指向不同的数据),但它们指向的数据不能被修改。

07-25 18:05