C at Sea

If below questions seem alien, think again if you are ready!!


Determine if stack grows up/down (low to high / high to low memory)?

Does below work?
int* f1() {   char c;   return &c;  }
int* f2() {  char c;    return &c; }
main()
{  int *a1=f1();  int *a2=f2();  /*Get diff of a1 and s2 to determine*/ }

Obviously above does not work, you get both values b3375000 b3375000. Heard of virtual memory? Same address re-used

You need to call function from function to actually get stack direction.
int does_stack_grow_to_high_mem()
{
  char c1;  //f1b33727
  int *p=f1(); //f1b3370f 
  if(c1>p) return 1;
  else       return 0;
}     

How to do it in single function?
void does_stack_grow_to_high_mem(int *ptr) {
    int i;
    if (!ptr)                                     does_stack_grow_to_high_mem(&i);
    else if (ptr < &i)           return 1; //yes to high memory
    else                                           return 0;
}
does_stack_grow_to_high_mem(NULL);

Determine how much stack size a function uses?

This can be read in output of assembler.
main() {    int i=10;    printf("%d", i);    }
# gcc -S prog.c
# vi prog.s  -> Look for subq. In this case 16 Bytes are taken.
main:
.LFB0:
...
        .cfi_def_cfa_register 6
        subq    $16, %rsp

main() {    int i=10;    int j[1024];    printf("%d", i);    }


main:

.LFB0:

        subq    $4112, %rsp  //Increased by 1024*4=4096


main() {    int i=10;    int j[1024]={0};    printf("%d", i);    }
main:
.LFB0:
...
        pushq   %rbx
        subq    $4120, %rsp  //Increased by 8 Bytes when it is initialized

main() {    int i=10;      }
You shall see no stack [subq] is used in .s file as compiler optimized unused variable i.

Find machine is little or big endian?

0xABCD stored as DCBA (BE, MSB at lower address), ABCD (LE, LSB at Lower address)

So many ways:
unsigned int x=1;
printf "%d", (int) ( ( (char*) &x) [0] ));

isBE()
    union {
        unsigned int i;
        char c[4];
    } endian = { 0x01000000 };
    return endian.c[0];

 int x=1;
if(*(char*)&x)==1) LE
  • Define a macro for milli-seconds in a day.
  • Declare an array size 64K:   #define SIZE (1<<16)
  • Swap two numbers,
    void swap(int *ptrx, int *ptry) //smartness case if(ptrx==ptry) return; //For case e.g. quick sort when var is swapped with self

Which variable goes in stack/BSS/ReadOnlyData Segment?

#include "stdio.h"


static int g_static_x;                  //BSS
static int g_static_x_init=0xCAFECAFE;  //DATA
static int g_static_x_init0=0;          //BSS

const int g_const_x;                    //BSS //Can be modified via ptr
const int g_const_x_init=0xDEADBEEF;    //ReadOnly Data //crash if modified via ptr
const int g_const_x_init0=0;            //ReadOnly Data //crash if modified via ptr

int g_x;                                //BSS
int g_x_init=0xDEADBEAD;                //BSS
int g_x_init0=0;                        //BSS

main()
{

  printf("Global       : g_static_x       [@%p]=0x%x\n", &g_static_x, g_static_x );
  printf("Global       : g_static_x_init  [@%p]=0x%x\n", &g_static_x_init, g_static_x_init);
  printf("Global       : g_static_x_init0 [@%p]=0x%x\n\n", &g_static_x_init0, g_static_x_init0);

  printf("Global const : g_const_x        [@%p]=0x%x\n", &g_const_x, g_const_x);
  printf("Global const : g_const_x_init   [@%p]=0x%x\n", &g_const_x_init, g_const_x_init);
  printf("Global const : g_const_x_init0  [@%p]=0x%x\n\n\n", &g_const_x_init0, g_const_x_init0);

  static int l_static_x;                //BSS
  static int l_static_x_init=0xDEADCAFE;//BSS
  static int l_static_x_init0=0;        //Data

  const int l_const_x;                  //Stack //Can be modified via ptr
  const int l_const_x_init=0xBEEFBEEF;  //Stack //Can be modified via ptr
  const int l_const_x_init0=0;          //Stack //Can be modified via ptr

  printf("Local        : l_static_x       [@%p]=0x%x\n", &l_static_x, l_static_x );
  printf("Local        : l_static_x_init  [@%p]=0x%x\n", &l_static_x_init, l_static_x_init);
  printf("Local        : l_static_x_init0 [@%p]=0x%x\n\n", &l_static_x_init0, l_static_x_init0);

  printf("Local const  : l_const_x        [@%p]=0x%x\n", &l_const_x, l_const_x);
  printf("Local const  : l_const_x_init   [@%p]=0x%x\n", &l_const_x_init, l_const_x_init);
  printf("Local const  : l_const_x_init0  [@%p]=0x%x\n\n\n", &l_const_x_init0, l_const_x_init0);

  char str[]="i-techh";                 //Stack
  char *p_str="i-techh";                //Stack

  static char static_str[]="i-techh";   //Data
  static char *static_p_str="i-techh";  //pointing to address in text segment


  printf("Local        : str[]           [@%p]=%s\n", &str[0], str );
  printf("Local        : p_str           [@%p]=%s\n", &p_str, p_str);
  printf("Local        : static_str[]    [@%p]=%s\n", &static_str[0], static_str);
  printf("Local const  : static_p_str    [@%p]=%s\n\n", &static_p_str, static_p_str);

}
/*
Global       : g_static_x       [@0x600d8c]=0x0
Global       : g_static_x_init  [@0x600d58]=0xcafecafe
Global       : g_static_x_init0 [@0x600d90]=0x0

Global const : g_const_x        [@0x600da0]=0x0
Global const : g_const_x_init   [@0x4007b8]=0xdeadbeef
Global const : g_const_x_init0  [@0x4007bc]=0x0


Local        : l_static_x       [@0x600d94]=0x0
Local        : l_static_x_init  [@0x600d60]=0xdeadcafe
Local        : l_static_x_init0 [@0x600d98]=0x0

Local const  : l_const_x        [@0x7fff29b8bbbc]=0x0
Local const  : l_const_x_init   [@0x7fff29b8bbb8]=0xbeefbeef
Local const  : l_const_x_init0  [@0x7fff29b8bbb4]=0x0


Local        : str[]           [@0x7fff29b8bba0]=i-techh
Local        : p_str           [@0x7fff29b8bb98]=i-techh
Local        : static_str[]    [@0x600d64]=i-techh
Local const  : static_p_str    [@0x600d70]=i-techh

 */

This can be checked using objdump -t a.out

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000              which_var_goes_where.c
0000000000600d8c l     O .bss   0000000000000004    g_static_x
0000000000600d58 l     O .data  0000000000000004   g_static_x_init
0000000000600d90 l     O .bss   0000000000000004    g_static_x_init0
0000000000600d94 l     O .bss   0000000000000004    l_static_x.2059
0000000000600d60 l     O .data  0000000000000004    l_static_x_init.2060
0000000000600d98 l     O .bss   0000000000000004    l_static_x_init0.2061
0000000000600d64 l     O .data  0000000000000008   static_str.2067
0000000000600d70 l     O .data  0000000000000008   static_p_str.2068
0000000000600d5c g     O .data  0000000000000004   g_x_init
0000000000600d9c g     O .bss   0000000000000004    g_x
0000000000600d88 g     O .bss   0000000000000004    g_x_init0
00000000004007b8 g     O .rodata 0000000000000004  g_const_x_init
0000000000600da0 g     O .bss   0000000000000004    g_const_x
00000000004004c4 g     F .text  00000000000001f8      main
0000000000400390 g     F .init  0000000000000000      _init

* static local variables have .number added and have address outside stack space.
* Other local variables are not seen in symbol table e.g. l_const_x, str[], p_str[] etc

Following actions generate compilation error: assignment of read-only variable:
/*
g_const_x=0;   g_const_x_init=0x10;   g_const_x_init0=0
l_const_x=0;  l_const_x_init=0x10l l_const_x_init0=10
*/

Extending this problem further to understand what const variables can be modified using pointer, but generates warning: assignment discards qualifiers from the pointer type
int *p;
p=&g_const_x;  *p=10;       //does not seg fault

p=&l_const_x;   *p=10;       //does not seg fault

p=&l_const_x_init0; *p=10; //does not seg fault



Below generate warning as well as seg faults.

p=&g_const_x_init0; *p=10; //seg fault
p=&g_const_x_init; *p=10; //seg fault


p=&l_const_x_init; *p=10; //seg fault





Fork Concepts: Find output of below commands on GCC


#include "stdio.h"


main()

{

        int a=10;

        int b=10;
        char *aa=&a;
        int *ptr1=malloc(10);
        int cpid=fork();
        int *ptr2=malloc(20);
        int *ptr3=malloc(30);
        if(cpid==0)
        {
                printf("\tcpid0 a:%d b:%d\n", a, b);
                a++; b++;
                sleep(1);
                printf("\tcpid0 a:%d b:%d\n", a, b);
                printf("\tcpid0 PTR %p %p %p\n", ptr1, ptr2, ptr3);
                *ptr1=100;
                *ptr2=200;
                *ptr3=300;
                printf("\tcpid0 PTRVAL %d %d %d\n", *ptr1, *ptr2, *ptr3);
                sleep(3);
                printf("\tcpid0 PTRVAL %d %d %d\n", *ptr1, *ptr2, *ptr3);
        }
        else
        {
                printf("cpid non0: a:%d b:%d\n", a, b);
                a+=2;
                b+=2;
                sleep(1);
                printf("cpid non0: a:%d b:%d\n", a, b);
                *ptr1=10;
                *ptr2=20;
                *ptr3=30;
                printf("cpid non0 PTR :%p %p %p\n", ptr1, ptr2, ptr3);
                printf("cpid non0 PTRVAL:%d %d %d\n", *ptr1, *ptr2, *ptr3);
                sleep(5);
                printf("cpid non0 PTRVAL:%d %d %d\n", *ptr1, *ptr2, *ptr3);
        }

}

Output
 cpid non0: a:10 b:10
        cpid0 a:10 b:10
cpid non0: a:12 b:12
cpid non0 PTR :0x2393010 0x2393030 0x2393050
cpid non0 PTRVAL:10 20 30
        cpid0 a:11 b:11
        cpid0 PTR 0x2393010 0x2393030 0x2393050
        cpid0 PTRVAL 100 200 300
        cpid0 PTRVAL 100 200 300
cpid non0 PTRVAL:10 20 30


Spot Errors: C Tricks

1.
main()
{ static int i;    for(i=0;i;i--) main() }
Output: Seg Fault. i=0 is assignment not initialization. Every time i is 5.
Does it Fix?  { static int i=5;    for(i;i;i--) main() }    NO.

2.

char *getstring1()  { char str[]="Alas! I cant be printed"; return str; }
char *getstring2() { char *str="Hurray!! I shall be shown"; return str;}
main(){ printf("str1: %s, str2:%s\n", getstring1(), getstring2()); }
Output: str1: b@, str2:Hurray!! I shall be shown

3.
struct s{char c[0];};     sizeof(struct s) : 0

4.

register int i=10; int *ptr=&i;   //error: address of register variable requested

5.

void *void_ptr, v; v=0;   //error: variable or field 'v' declared void
void_ptr=&v;  printf("%v", *vptr); //error: invalid use of void expression

6. Fix this as it does not actually swap strings

swap(char *str1, char *str2){ char*tmp=str1; str1=str2; str2=tmp; }
main(){ char s1[]="Hi", s2[]="Hello";  swap(s1, s2);  }

Fix:  ??


7. Modulus operator follows numerator sign

3%-8  = 3  
-3%-8 = -3
-3%8 = -3
3%-8/2 = 3/2=1

8.

int i=10;    static int j=i;  //error: initializer element is not constant

9. float a; (int)a=20; / / error: lvalue required as left operand of assignment


10. error: macro names must be identifier

#ifdef 1
int some=10;
#endif

11. valid. some=0.

#define M 0  //or any value 1, or any string "hi"
#ifdef M
int some=0;
#else
#int some=1;
#endif

12. Replace #ifdef with #if

Result different. M=0 -> some=1;  M=10 -> some=0;  M="hi" -> error: token ""hi"" is not valid in preprocessor expressions

13.



Spot Errors: switch-case tricks

Compiler implements jump table for cases in switch.

Case expression can not be string, switch condition can be string.
e.g. switch( *(1+"AB""CD"+1))  //2+"ABCD" = "ABCD"[2] = 'C' //legal.

Case expression is int. If float given in case expression, it is type casted to int.

1.
switch(0x0)
{ case NULL:  case 0:   case '\0': } //error: duplicate case not allowed.
int i=0;

2.
switch(i)
{
  case 1: int j=10; break;
  case 2: int j=10; break;
}
switch.c:9: error: a label can only be part of a statement and a declaration is not a statement
switch.c:10: error: a label can only be part of a statement and a declaration is not a statement
switch.c:10: error: redefinition of 'j'

switch.c:9: note: previous definition of 'j' was here


3.
int i=0;

switch(i)
{
  case 1: int j=10; break;
  case 2: j=10; break;
}
//error: a label can only be part of a statement and a declaration is not a statement

4. Will there be any jump for switch in below code assembly?
int i=0;
switch(i)
{
  case 1: break;
  case 2: break;
}
Ans: NO.

5. Objdump for below code:

int i=0, j;
switch(i)
{
  case 1: j=10; break;
  case 2: j=20; break;
}
0000000000400474:

  400474:       55                      push   %rbp

  400475:       48 89 e5                mov    %rsp,%rbp
  400478:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
  40047f:       8b 45 f8                mov    -0x8(%rbp),%eax
  400482:       83 f8 01                cmp    $0x1,%eax
  400485:       74 07                   je     40048e
  400487:       83 f8 02                cmp    $0x2,%eax
  40048a:       74 0b                   je     400497
  40048c:       eb 10                   jmp    40049e
  40048e:       c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
  400495:       eb 07                   jmp    40049e
  400497:       c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
  40049e:       c9                      leaveq
  40049f:       c3                      retq

?? If we add default case, then there are many nop added at the end of main objdump.

6. 
int i=0,j, k=20;
switch(i)
{
  k=10; //not executed
  default: j=40;
  case 1: j=10; break;
  case 2: j=20; break;
}
printf("k:%d", k);

Output: compiles fine. k:20
If there was printf statement in place of k=10, it would not be printed.



Print 1-100 using five threads, each thread prints different series, 

e.g. Thread0: 1,6,11          Thread1: 2,7,12          Thread2: 3,8,13   ...
Can this be done using single thread function?

#include "stdio.h"
#include "pthread.h"
#define NUM_THREAD 5
#define START 1
#define END 100

int n=START; //starting value

int wasted_cycles[NUM_THREAD];

pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_func(void*data)
{
        int num = * ( (int*)data );
        char thrname[20] = {0};
        int comparewith = ( num%NUM_THREAD );

        sprintf(thrname, "%s%02d", "thread", num);
        pthread_mutex_lock(&fastmutex);
        while(n<=END)
        {
                if( (n%NUM_THREAD) == comparewith)
                {
                        printf("%s: %03d \t", thrname, n);
                        if( ( (n>0)&&(n%NUM_THREAD)) ==0 )
                                printf("\n");
                        n++;
                }
                else
                {  wasted_cycles[num-1]++; }
                pthread_mutex_unlock(&fastmutex);
                sched_yield();
        }
        free(data);
}
main()
{

        pthread_t pid[NUM_THREAD+1];
        int i;
        for(i=1;i<=NUM_THREAD;i++)
        {
                int *arg = malloc(sizeof(*arg));
                *arg=i;
                pthread_create(&pid[i], 0, thread_func, (void*)arg);
                //printf("Created thread:%d", i);
        }
        for(i=1;i<=NUM_THREAD;i++)
                pthread_join(pid[i], 0);

        for(i=0;i
                printf("\nWasted(Thread%d): %d", i+1, wasted_cycles[i]);
}
Output
thread01: 001   thread02: 002   thread03: 003   thread04: 004   thread05: 005
thread01: 006   thread02: 007   thread03: 008   thread04: 009   thread05: 010
thread01: 011   thread02: 012   thread03: 013   thread04: 014   thread05: 015
thread01: 016   thread02: 017   thread03: 018   thread04: 019   thread05: 020
thread01: 021   thread02: 022   thread03: 023   thread04: 024   thread05: 025
thread01: 026   thread02: 027   thread03: 028   thread04: 029   thread05: 030
thread01: 031   thread02: 032   thread03: 033   thread04: 034   thread05: 035
thread01: 036   thread02: 037   thread03: 038   thread04: 039   thread05: 040
thread01: 041   thread02: 042   thread03: 043   thread04: 044   thread05: 045
thread01: 046   thread02: 047   thread03: 048   thread04: 049   thread05: 050
thread01: 051   thread02: 052   thread03: 053   thread04: 054   thread05: 055
thread01: 056   thread02: 057   thread03: 058   thread04: 059   thread05: 060
thread01: 061   thread02: 062   thread03: 063   thread04: 064   thread05: 065
thread01: 066   thread02: 067   thread03: 068   thread04: 069   thread05: 070
thread01: 071   thread02: 072   thread03: 073   thread04: 074   thread05: 075
thread01: 076   thread02: 077   thread03: 078   thread04: 079   thread05: 080
thread01: 081   thread02: 082   thread03: 083   thread04: 084   thread05: 085
thread01: 086   thread02: 087   thread03: 088   thread04: 089   thread05: 090
thread01: 091   thread02: 092   thread03: 093   thread04: 094   thread05: 095
thread01: 096   thread02: 097   thread03: 098   thread04: 099   thread05: 100

Wasted(Thread1): 293
Wasted(Thread2): 69
Wasted(Thread3): 300
Wasted(Thread4): 68
Wasted(Thread5): 141

The wasted cycles will vary across executions, confirming randomness in thread scheduling, e.g.
Wasted(Thread1): 3372
Wasted(Thread2): 3582
Wasted(Thread3): 2918
Wasted(Thread4): 3513

Wasted(Thread5): 0 


Do you see any CPU wastage in above implementation, ideas on how fix?



C again,

void *realloc(void *ptr, size_t size) may
              move data to new pointer and free old pointer, return new pointer, OR
              keep old pointer, extend it by given size and return old pointer.
              Newly allocated memory is uninitialized, realloc(NULL, 10) is safe

write() is faster for writing data to file compared to printf, fprintf, as parsing is required before calling low level write function.

memcpy() is faster than strcpy() as it does not have to check each byte for NULL.

const char *ptr="Hello";    ptr="Cielo"; (OK)     ptr[0]='a';(ERR, ptr is pointer to const char)




Some blogs I really like to follow:

http://embeddedgurus.com/barr-code/


No comments:

Post a Comment