Category Archives: C_language

C qualifier — volatile

volatile is mostly used in low-level C programming, when the code deals with peripheral devices, IO ports, ISRs.

With “volatile”, compiler will not do any optimisation (e.g. store the value in a register and read it back from the register) on this variable , but read the variable from the memory every time.

a few examples:

  1. a variable which contains the value of a register.
  2. Global variables modified by an interrupt service routine outside the scope.
  3. Global variables within a multi-threaded application.
Advertisements

char pointer(3): const

To have an idea what is __const__ in one char pointer declaration, read it from right to left.

  • const char *p
    • p is a pointer to a const char, it is same as “char const *p.
    • you can change the value of p and get it to point to different constant characters. But whatever p points to, you can not change the contents.
  • char * const p
    • p is a const pointer to a char
    • you are unable to change the pointer, but can change the contents.

Example 1

if it good to do:

norm = str;

norm = “AUTO”.

if you define norm this way:

const char *norm = “NSTC_J”;

char str[] = “PAL_M”;

However, both will fail with error “assignment of read-only variable ‘norm’”, if you define

char * const norm = “NSTC_J”,

Example 2

I have

struct norm_val {

    char *norm;

    Int reg_val;

};

struct norm_val set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

As we want to keep the contents of set_norms[i].norm unmodifiable, we need define “norm” as a pointer to a const char:  const char *norm;

At the same time, we don’t want set_norm[i].norm points to somewhere else, we need define “norm” as a const pointer:  char * const norm;

Moreover, we don’t want the entire content of set_norms[] being changed, either “norm” pointer, or “reg-val” value, we need define the set_norms as const (by doing this, we don’t need explicitly define “norm” to a const pointer).

struct norm_val {

    const char *norm;

    int reg_val;

} ;

const struct norm_val set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

Or

const struct {

    const char *norm;

    int reg_val;

} set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

C language basics(1): initalization of objects

There are two sorts of initialization:

  • at compiler time
    • The object with static duration: global variables, marked as static, extern, can only be initialized at compile time.
    • compile-time initialization can only be done using constant expressions.
  • at run time.
    • Any other object has automatic duration, and can only be initialized at run time.
    • can be done using any expressions.

“all or nothing” approach (to aggregate initializations): an attempt to explicitly initialize any part of the aggregate, will guarantee that the entire aggregate be initialized. The parts without an explicit initializer will be zero-initialized:

pointer: NULL.

Arithmetic type: positive zero. (for character type, c = 0 equals to c = ‘\0’) ‘\0’ is to emphasize the character nature of some expression.

Aggregate: every member is initialized based on its type.

Union: the first name number is initialized based on its type.

 

This rule explains this fact: “When you initialize a character array with a string, the system adds ‘\0’ to the end of the string, by default.”

If you define char str[4] = “123”, the compiler initializes str[3] to zero.

If you define char str[4] = “1234”, str[3] is ‘4’, and there is no ending ‘\0’.

If you define char str[4] = “12345”, the compiler will warn that “initializer-string for array of chars is too long”. The interesting facts are: strlen(str) is 5, sizeof(str) is 4, when you use printf(“%s”, str), it prints “1234”, without “5”.

But I don’t understand why 5 is still not there, even I print the chars one by one, printf(“%c”, str[4]) is nothing….

If I define char str[2][4] = {“12345”}, the compiler warns, strlen(str[0]) is 4, sizeof(str) is 8. Printf(“%s#%s”, str[0], sre[1]) will give you “1234#”.

If I define char str[2][4] = {“12345”, “123”}, warning, strlen(str[0]) = 7, sizeof(str) is 8. Printf(“%s#%s”, stre[9], str[1]) will give you “1234123#123);

String_5[2][4] = {“1234”};

String_5[1] is initialized the same as objects that have static storage duration.

References:

http://publications.gbdirect.co.uk/c_book/chapter6/initialization.html

char pointer(2): passing a pointer as argument

The library provides APIs:

xxx_set_property_p(ctx, int property, void *value);

xxx_get_property_p(ctx, int property, void **value);

In the library,

struct buf {

    int index;

    int pointer;

}

struct context {

    void ** buf_pointers;   // point to the start of the pointers, which point to the application-allocated buffers

    struct buf * xxx_bufs;   // point to start of

    int planar_offsets[3];

    const char *norm;

}

int xxx_set_property_p(ctx, int property, void *value)

{

    ctx->buf_pointers = (void **) value;

    memcpy(ctx->planar_offsets, (int *)value, sizeof(int) * 3);

    ctx->norm = (char *) value;

}

ctx->xxx_bufs = (struct buf *)calloc(num_bufs, sizeof(struct buf));

ctx->xxx_bufs[i].pointer = ctx->buf_pointers[i];

int xxx_get_property_p(ctx, int property, void **value)

{

    *value = ctx->planar_offsets; // ?????

    *value = ctx->buf_pointers;

    *value = ctx->norm;

}

 

In the application,

void ** pointers = NULL;

int planar_offsets[3];

char norm[100] = “”;

pointers = calloc (nbufs, sizeof(void *));

capture_set_property_p(ctx, PROP_BUFS, pointers);

capture_set_property_p(ctx, PROP_PLANAR, offsets);

capture_set_property_p(ctx, PROP_NORM, norm);

 

capture_get_property_p(ctx, PROP_BUFS, (void **)&pointers);

capture_get_property_p(ctx, PROP_NORM, (void **)&norm);

 

 

 

 

 

 

 

 

 

 

 

 

 

}

There are two ways to pass the norm to the library:

Char norm[128] = “ntsc_j”;  // note: use a large size, not leave the size blank, as you might have overbound issue after a later modification of the array

or

Char *norm= “nstc_j”;

(the change of #1 content can be done by strcpy(norm, “pal_m”), the change of #2 content can be done by redirecting the pointer “norm = “pal_m”;

If the norm will be taken from the input, the #1 declaration & initialization has to be used).

 

Capture_set_property_p(_PROPERTY_NORM, norm);

 

 

Capture_set_property_p(property, void *value), sometimes, we keep a copy of the array, as it is mandatory to keep capture driver running; if it is optional, we reply on the pointers supplied by the user, without keep our own copy.

 

In this function, we cast void *value to (char *)value, and validate it.

 

Char *norm = NULL;

Capture_get_property_p(PROPERTY_NORM, (void **)&norm);

 

In the library,

Capture_get_property_p(property, void ** value)

{

*(char **)value = “ntsc_j”;  // return a const string

}

 

Char pointer (1): array v.s. pointer

3 common methods to declare and initialize a string:

  • char string_1[9] = “01234”;
    • declare string_1 as an array of 9 characters.
    • set the initial content of element [0] to [4].
    • the remaining elements [6],[7],[8] is initialized implicitly to zero.
    • we may later overwrite with other characters if we wish, via strcpy().
  • char string_2[  ]= ”01234”;
  • char *string_3   = “01234”;
    • create a little block of characters (somewhere in memory), and let the string_3 pointer point to it.
      • If we do “string_3 = “678””, printf(“%s”, string_3) will give you “678”.
    • we may reassign string_3 to point somewhere else, but we can’t modify the characters it points to.
      • If we do “strcpy(string_3, “567”), the program will crash.
      • If we do “string_3[0]=’5’, string_3[1]=’6’”, there is no crash, but it doesn’t work at all:  if you printf(“%c”, string3_[0]), it is still ‘0’.
      • If you initialize the pointer this way: char *string_3 = {‘1’, ‘2’, ‘\0’},  the compiler will give you a few warnings, like “initialization makes pointer from integer without a cast”, “excess elements in scalar initializer”;
      • if you do char *string_3={‘1’}, only the first warning still there.
    •  Be cautious of using “char *string = “1234””, it is dangerous/bad practice to use it, as the string is un-modifiable, and possibly you don’t reallize it until you encounter problems.

 Note: Either string_1, or string_2, or string_3, the compiler appends a ending/terminating char ‘\0’ automatically.

sizeof()               strlen()

string_1                  9                           5

string_2                  6                           5

string_3                  4                            5

 More notes:

  1. sizeof(*string_3) is 1.
  2. strlen(str) looks for ‘\0’, and return the length (not including ‘\0’).
  3. trcpy(str1, str2) copies str2 to str1, until ‘\0’ is reached, and then set the last index to ‘\0’.
  4. printf(%s”, str) either stops at ‘\0’, or stops at the number of the elements.
    • looks for ‘\0’, in order to decide the end of s string; at the same time,
    • if the sizeof the array is initialized bigger than the number of elements it was defined to, printf stops at the last element of the array.

More about the storage of a string

  1. The endianness doesn’t affect the storage of C string (character array), as in a C array, the address of consecutive array elements always increases: &a[i+1] is bigger than &[i].