Week 37 / 2022

Mohamed Saif published on
16 min, 3018 words

C: chapter 2

  • C, like most languages, does not specify the order in which the operands of an operator are evaluated. (The exceptions are &&, ||, ?:, and ,'.) For example, in a statement like x = f() + g(); fmay be evaluated beforegor vice versa; thus if eitherforgalters a variable on which the other depends,x` can depend on the order of evaluation. Intermediate results can be stored in temporary variables to ensure a particular sequence.
  • Similarly, the order in which function arguments are evaluated is not specified, so the statement printf("%d %d\n", ++n, power(2, n)); /_ WRONG _/ can produce different results with different compilers, depending on whether n is incremented before power is called.

C: chapter 3

  • The standard library provides a more elaborate function strtol for conversion of strings to long integers
  • One final C operator is the comma (,), which most often finds use in the for statement. A pair of expressions separated by a comma is evaluated left to right, and the type and value of the result are the type and value of the right operand. Thus in a for statement, it is possible to place multiple expressions in the various parts, for example to process two indices in parallel. `for (i = 0, j = strlen(s)-1; i < j; i++, j--)``.
  • The commas that separate function arguments, variables in declarations, etc., are not comma operators, and do not guarantee left to right evaluation.
  • By contrast, the third loop in C, the do-while, tests at the bottom after making each pass through the loop body; the body is always executed at least once. The syntax of the do is do statement while (expression); The statement is executed, then expression is evaluated. If it is true, statement is evaluated again, and so on.
  • Nevertheless, there are a few situations where gotos may find a place. The most common is to abandon processing in some deeply nested structure, such as breaking out of two or more loops at once. The break statement cannot be used directly since it only exits from the innermost loop. This organization is handy if the error-handling code is non-trivial, and if errors can occur in several places.
    for ( ... )
        for ( ... ) {
            ...
            if (disaster)
                goto error;
            }
        ...
    error: /* clean up the mess */
        ...
  • With a few exceptions like those cited here, code that relies on goto statements is generally harder to understand and to maintain than code without gotos.

C: Chapter 4

  • Source files may be compiled separately and loaded together, along with previously compiled functions from libraries. We will not go into that process here, however, since the details vary from system to system.

  • Function declaration and definition is the area where the ANSI standard has made the most changes to C.

  • This makes it possible for a compiler to detect many more errors than it could before. Furthermore, when arguments are properly declared, appropriate type coercions are performed automatically.

  • The standard library provides a function strstr, returns a pointer instead of an index.

  • If the return type is omitted, int is assumed.

  • The functions can occur in any order in the source file, and the source program can be split into multiple files, so long as no function is split.

  • The expression will be converted to the return type of the function if necessary.

  • The mechanics of how to compile and load a C program that resides on multiple source files vary from one system to the next.

  • <stdlib.h>.atof converts the string s to its double-precision floating-point equivalent

  • If the function takes arguments, declare them; if it takes no arguments, use void.

  • The adjective external is used in contrast to internal, which describes the arguments and variables defined inside functions. External variables are defined outside of any function, and are thus potentionally available to many functions.

  • Functions themselves are always external, because C does not allow functions to be defined inside other functions

  • external variables are globally accessible, they provide an alternative to function arguments and return values for communicating data between functions.

  • If a large number of variables must be shared among functions, external variables are more convenient and efficient than long argument lists.

  • however, this reasoning should be applied with some caution, for it can have a bad effect on program structure, and lead to programs with too many data connections between functions.

  • Example of section 4.3?? calculator program

  • issues that arise in larger programs?

  • The scope of an external variable or a function lasts from the point at which it is declared to the end of the file being compiled.

  • the variables sp and val may be used in push and pop simply by naming them; no further declarations are needed. But these names are not visible in main, nor are push and pop themselves. On the other hand, if an external variable is to be referred to before it is defined, or if it is defined in a different source file from the one where it is being used, then an extern declaration is mandatory.

      main() { ... }
      int sp = 0;
      double val[MAXVAL];
    
      void push(double f) { ... }
      double pop(void) { ... }
    
  • It is important to distinguish between the declaration of an external variable and its definition. A declaration announces the properties of a variable (primarily its type); a definition also causes storage to be set aside.

  • If the lines int sp; double val[MAXVAL]; appear outside of any function, they define the external variables sp and val, cause storage to be set aside, and also serve as the declarations for the rest of that source file. On the other hand, the lines extern int sp; extern double val[]; declare for the rest of the source file that sp is an int and that val is a double array (whose size is determined elsewhere), but they do not create the variables or reserve storage for them.

  • There is one more thing to worry about - the definitions and declarations shared among files. As much as possible, we want to centralize this, so that there is only one copy to get and keep right as the program evolves. Accordingly, we will place this common material in a header file

  • The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled.

  • Static storage is specified by prefixing the normal declaration with the word static

  • Normally, function names are global, visible to any part of the entire program. If a function is declared static, however, its name is invisible outside of the file in which it is declared.

  • Internal static variables are local to a particular function just as automatic variables are, but unlike automatics, they remain in existence rather than coming and going each time the function is activated. This means that internal static variables provide private, permanent storage within a single function.

  • In the absence of explicit initialization, external and static variables are guaranteed to be initialized to zero; automatic and register variables have undefined (i.e., garbage) initial values.

  • the initialization is done once, conceptionally before the program begins execution.

  • To initialize an array days with the number of days in each month: int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } When the size of the array is omitted, the compiler will compute the length by counting the initializers, of which there are 12 in this case.

  • Character arrays are a special case of initialization; a string may be used instead of the braces and commas notation: char pattern = "ould"; is a shorthand for the longer but equivalent char pattern[] = { 'o', 'u', 'l', 'd', '\0' }; In this case, the array size is five (four characters plus the terminating '\0').

  • When a function calls itself recursively, each invocation gets a fresh set of all the automatic variables, independent of the previous set.

  • The standard library includes a version of qsort that can sort objects of any type.

  • Recursion may provide no saving in storage, since somewhere a stack of the values being processed must be maintained. Nor will it be faster. But recursive code is more compact, and often much easier to write and understand than the non-recursive equivalent. Recursion is especially convenient for recursively defined data structures like trees

  • C provides certain language facilities by means of a preprocessor, which is conceptionally a separate first step in compilation.

  • If the filename is quoted, searching for the file typically begins where the source program was found; if it is not found there, or if the name is enclosed in < and >, searching follows an implementation-defined rule to find the file. An included file may itself contain #include lines.

  • (Strictly speaking, these need not be files; the details of how headers are accessed are implementation-dependent.)

  • Any name may be defined with any replacement text. For example #define forever for (;;) /* infinite loop */ defines a new word, forever, for an infinite loop.

  • It is also possible to define macros with arguments, so the replacement text can be different for different calls of the macro.

  • Some care also has to be taken with parentheses to make sure the order of evaluation is preserved??

  • Names may be undefined with #undef, usually to ensure that a routine is really a function, not a macro

    #undef getchar

    int getchar(){...}

  • Formal parameters are not replaced within quoted strings. If, however, a parameter name is preceded by a # in the replacement text, the combination will be expanded into a quoted string with the parameter replaced by the actual argument. #define dprint(expr) printf(#expr " = %g\n", expr)
  • ##?
  • It is possible to control preprocessing itself with conditional statements that are evaluated during preprocessing. This provides a way to include code selectively, depending on the value of conditions evaluated during compilation.
  • The expression defined(name) in a #if is 1 if the name has been defined, and 0 otherwise. #if !defined(HDR) = #ifndef HDR

Textual: Interactive man page.

  • python -m textual.app, for quick test after installation.

Graph Database and GraphBlas

  • Graph databases are commonly referred to as a NoSQL.
  • Relationships are a first-class citizen in a graph database and can be labelled, directed, and given properties.
  • In addition to having query language interfaces, some graph databases are accessed through APIs.
  • OLTP (online transaction processing ) vs OLAP (online analytical processing)???
  • It consists of a set of objects, which can be a node or an edge.
  • GraphBLAS implementations have also been used in high-performance graph database applications such as Redis
  • Basic Linear Algebra Subprograms (BLAS): a specification that prescribes a set of low-level routines for performing common linear algebra operations such as vector addition, scalar multiplication, dot products, linear combinations, and matrix multiplication.
  • You’ll find that everything is an interconnected graph of relationships.

Cryptography

  • Cryptography is a process of converting a plain text into an encrypted message and then decrypting it again to plain text by intended users.

quotes

  • scientific discovery is not as straightforward and smooth as textbooks
- من غير نية، تخسر!
- الصبر مع الله (وهو دوران العبد مع مراد الله الديني منه، ومع أحكامه الدينية صابرًا نفسه معها، سائرًا بسيرها مقيمًا بإقامتها، يتوجه معها أين توجهت ركائبها، وينزل معها أين استقلت مضاربها، فهذا معنى كونه صابرًا مع الله، أي قد جعل نفسه وقفًا على أوامره ومحابِّه، وهو أشد أنواع الصبر وأصعبها، وهو صبر الصديقين)، الحسبة مش مكتملة؟ ربنا هيكملهالك! الشريعة بتقول كدا مليش فيه!
- المجتمع تفور قدوره بالتوحيد.
- أنا لك.

Assembly

  • There are different assembly "flavors" for different CPU classes. For example, there is x86 assembly, x86-64 assembly, Motorola 68000 assembly, ARM assembly, Z80 assembly, and many others.
  • assembly requires you to understand how a CPU works. You must learn about registers, segments (data, bss, text), the heap, the stack, memory addressing modes, etc.
  • NASM, MASM, YASM, GAS, and HLA are all different assemblers for the x86 CPU. They are all x86 assembly, and produce the same machine code, but each one runs for a certain OS (NASM for Linux, MASM for Windows) and may use either the Intel or AT&T syntax.
  • Most assembly programmers prefer the Intel syntax over the AT&T syntax as it's clearer to read and program with.
  • The most popular (and my choice of) assembler is NASM. It runs both on Windows and Linux, uses the Intel syntax, and is Free Software.
  • reddit comment

C: chapter 5

  • A pointer is a variable that contains the address of a variable. Pointers are much used in C, partly because they are sometimes the only way to express a computation, and partly because they usually lead to more compact and efficient code than can be obtained in other ways.
  • A pointer is a group of cells (often two or four) that can hold an address.
  • The & operator only applies to objects in memory: variables and array elements. It cannot be applied to expressions, constants, or register variables.
  • The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to.
  • The declaration of the pointer ip, int *ip; is intended as a (mnemonic?); it says that the expression *ip is an int.
  • Every pointer points to a specific data type. (There is one exception: a pointer to void is used to hold any type of pointer but cannot be dereferenced itself.
  • If ip points to the integer x, then *ip can occur in any context where x could
  • (*ip)++: The parentheses are necessary in this last example; without them, the expression would increment ip instead of what it points to, because unary operators like * and ++ associate right to left.
  • pointers can be used without dereferencing, since they are variables.
  • Since C passes arguments to functions by value, there is no direct way for the called function to alter a variable in the calling function. The way to obtain the desired effect is for the calling program to pass pointers to the values to be changed: func(&a, &b);. Pointer arguments enable a function to access and change objects in the function that called it.
  • Since the operator & produces the address of a variable, &a is a pointer to a.
  • getint return the end of file status as its function value, while using a pointer argument to store the converted integer back in the calling function. This is the scheme used by scanf
  • In C, there is a strong relationship between pointers and arrays, strong enough that pointers and arrays should be discussed simultaneously. Any operation that can be achieved by array subscripting can also be done with pointers.
  • If pa is a pointer to an integer, declared as int *pa; then the assignment pa = &a[0]; sets pa to point to element zero of a; that is, pa contains the address of a[0]. x = *pa; will copy the contents of a[0] into x.
  • If pa points to a particular element of an array, then by definition pa+1 points to the next element, pa+i points i elements after pa, and pa-i points i elements before. These remarks are true regardless of the type or size of the variables in the array a. The meaning of adding 1 to a pointer, and by extension, all pointer arithmetic, is that pa+1 points to the next object, and pa+i points to the i-th object beyond pa.
  • By definition, the value of a variable or (expression?) of type array is the address of element zero of the array. Thus after the assignment pa = &a[0]; pa and a have identical values. Since the name of an array is a synonym for the location of the initial element, the assignment pa = &a[0]; can also be written as pa = a;.
  • a[i] = *(a+i)
  • &a[i] = a+i
  • As the other side of this coin, if pa is a pointer, expressions might use it with a subscript; pa[i] is identical to *(pa+i). In short, an array-and-index expression is equivalent to one written as a pointer and offset.
  • As formal parameters in a function definition, char s[]; and char *s; are equivalent; we prefer the latter because it says more explicitly that the variable is a pointer.
  • It is possible to pass part of an array to a function, by passing a pointer to the beginning of the subarray
  • If one is sure that the elements exist, it is also possible to index backwards in an array; p[-1], p[-2], and so on are syntactically legal, and refer to the elements that immediately precede p[0].
  • pointer or address arithmetic, here we go!
  • C is consistent and regular in its approach to address arithmetic; its integration of pointers, arrays, and address arithmetic is one of the strengths of the language.
  • alloc, and afree: This shared array is private to alloc and afree. Since they deal in pointers, not array indices, no other routine need know the name of the array, which can be declared static in the source file containing alloc and afree, and thus be invisible outside it. In practical implementations, the array may well not even have a name; it might instead be obtained by calling malloc or by asking the operating system for a pointer to some unnamed block of storage.
  • ERROR: error: invalid operands to binary - (have ‘int’ and ‘char *’) if (0 + ARRLEN - free_spot >= num_of_req_slots){????
  • C guarantees that zero is never a valid address for data, so a return value of zero can be used to signal an abnormal event, in this case no space. Pointers and integers are not interchangeable. Zero is the sole exception: the constant zero may be assigned to a pointer, and a pointer may be compared with the constant zero. The symbolic constant NULL is often used in place of zero, as a mnemonic to indicate more clearly that this is a special value for a pointer. NULL is defined in <stdio.h>.