C语言点滴记录

Q1:    无符号数相减问题:

typedef unsigned short int Uint16;
Uint16 a=10;
Uint16 b=20;
那么Uint16 c= a-b得到65526
short int d=a-b得到-10

A: 数字在内存里存储的都是以补码形式存储的!正数的补码就是本身,负数的补码是其绝对值取反再加1.

无符号数Uint16为16位2进制,
a=10,2进制表示为:00000000 00001010 ;
b=20,2进制表示为:00000000 00010100 ;
相减得 :11111111 1110110换成10进制就为65526。

-10的补码:
1、取绝对值,得10,即0000 0000 0000 1010
2、取反,~0000 0000 0000 1010,得1111 1111 1111 0101
3、加1,1111 1111 1111 0101 + 1 = 1111 1111 1111 0110
即-10在内存里就是以1111 1111 1111 0110存储的,只不过看你怎么去读取而已。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Q2: malloc,realloc

把链表中的元素怎样快速导入到数组中?

int *array = (int *)malloc(sizeof(int));
int num = 0;
while(p)
{
num++;
array = (int *)realloc(array, sizeof(int)*num);
array[num-1] = p->val;
p=p->next;
}


Q3: i++ with ++i


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  

简单的来说,++i 和 i++,在单独使用时,就是 i=i+1。 

 a = ++i,相当于 i=i+1; a = i; (先i = i + 1,再使用i的值)而 

a = i++,相当于 a = i; i=i+1; (先使用i的值,再i = i + 1)

如果实在搞不明白++ --怎么回事,那也不是什么天塌下来的事。 因为

a=++i完全可以写成 i++; a=i

a=i++完全可以写成 a=i; i++

而且,这也是一种好的程序风格:++ -- 语句如非特殊需要,请单独一行使用。


Q4: C语言关键字volatile/const/register


 他们之间的共同点是都是编译器优化的keyword

编译器优化介绍 
     内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。

Volatile

How to Use C's volatile Keyword

这篇文章里解释的非常明白,他的blog里有不少好东西.简要的总结一下就是:

volatile 声明的变量, 它的值可能被改变,在什么情况下会被改变呢?一般只有2种可能:

1. 外设寄存器

不是全部寄存器,这里指的是那种可以被硬件外设改变的register,典型的比如: 中断状态寄存器,RX/TX data 寄存器等等,它们的值会随着硬件的状态而改变.

2.全局共享的变量

在task和task之间以及task和ISR之间,同时用同一个变量,一个被改变了,另一处却不知情.

为什么会这样呢,都是编译器优化搞得鬼,前文已经提到,一种典型的编译器优化方法就是将内存变量缓存到寄存器,这样编译器下次直接从寄存器中取值,永远不从内存中去取,所以对前面提到的这两种情况必然会出错.,因此,加上volatile关键字就是告诉编译器不要去优化这些变量,而是每次都从内存中去取值.

For instance:
uint8_t * pReg = (uint8_t *) 0x1234;
// Wait for register to become non-zero 
while (*pReg == 0) 
{ printf(".....");} 

编译器编译过后:
The assembly language now looks like this:

 mov ptr, #0x1234 
 mov a, @ptr
loop:
  bz loop

 The rationale of the optimizer is quite simple: having already read the variable's value into the accumulator (on the second line of
 assembly), there is no need to reread it, since the value will always be the same. Thus, in the third line, we end up with an 
infinite loop. To force the compiler to do what we want, we modify the declaration to:

 uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;

 The assembly language now looks like this:

 mov ptr, #0x1234

loop:
  mov a, @ptr
  bz loop

1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
         int square(volatile int *ptr)
         {
              return *ptr * *ptr;
         }


下面是答案:
    1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
    2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
    3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
    int square(volatile int *ptr)
    {
         int a,b;
         a = *ptr;
         b = *ptr;
         return a * b;
     }
    由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
     long square(volatile int *ptr)
     {
            int a;
            a = *ptr;
            return a * a;
     }

Register

要求编译器尽可能将变量存在CPU的内部寄存器中,不是绝对的.

memcpy (d, s, l)
  {
        register char *d;
      register char *s;
      register int i;
      while (i--)
          *d++ = *s++;
  }
Register的几点限制:

1.register变量必须是CPU所接受的类型

2.register变量可能不存放在内存中,所以不能用 & 操作来获取变量的地址,

3.只有局部变量和形式参数可以做为寄存器变量,其他不行如全局变量,static变量 register static int a;是错误的,局部变量在函数调用是占用在函数结束时,释放

4.CPU寄存器数量有限,不能定义任意多个寄存器变量


Q5: C语言笔试

链表相交-2

链表相交-3

list每个节点都有自己的内存空间节点之间用next指针相连,head是用来记录整个链表的首地址的  没有head的话怎么找第一个节点呢?list会用head来找头, 
 
  
typedef struct			/* Header for a linked list. */
    {
    DL_NODE *head;	/* header of list */
    DL_NODE *tail;	/* tail of list */
    } DL_LIST;
typedef struct _dlnode	/* Node of a linked list. */
    {
    struct _dlnode *next;	/* Points at the next node in the list */
    struct _dlnode *previous;/* Points at the previous node in the list */
    } DL_NODE;

list里会定义一个head节点来定位头,head 节点初始化成NULL((( DL_LIST *)( list))-> head = NULL; ),第一个真正node加进来时会赋值给head节点
((DL_LIST *)(list))->head = (DL_NODE *)(node); 因此list->head就指向list里的第一个真正node。

链表逆序

n&n-1

n&n-1 清除n最右边的1  

n&(n-1)作用:将n的二进制表示中的最低位为1的改为0,先看一个简单的例子:
n = 10100(二进制),则(n-1) = 10011 ==》n&(n-1) = 10000
可以看到原本最低位为1的那位变为0

n&-n取得n最右边的1 

这个意思: 输出n能够整除的 2的倍数的最大值 补码表示法, 就是n每位取反后再加1


memcpy

void * memcpy(void *dest, const void *src, unsigned int count)

container_of

#define container_of(ptr, type, member)

链表逆序

DL_NODE * reverse(DL_NODE * head)

atoi “345”->345

int atoi(const char * s)

32bit integer 翻转

unsigned Reverse( unsigned a )

两个排序数组合并

void MergeArray(int *pArray1, int nLen1, int *pArray2, int nLen2,int *pArray)

链表相交

node* find_node(node *head1, node *head2)

deadlock问题与避免

优先级翻转

中断嵌套






你可能感兴趣的:(c语言)