Search code examples
carrayspointersheader-filesdouble-pointer

C - char *' differs in levels of indirection from 'char (*)[200]


For a C programming assignment, I am trying to write several header files to check the syntax of a so called "X Programming Language". I have started very recently and I am writing the first header file. Here is the code I've written yet:

#ifndef _DeclarationsChecker_h_
#define _DeclarationsChecker_h_



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define LINE_LENGTH_LIMIT 200
#define CODE_LINE_LIMIT 1000


void checkDeclarations(char **code, int num_lines) {


    char *currentLine;

    for (int currentLineNum = 0; currentLineNum < num_lines; currentLineNum++) {

        if (code[currentLineNum] != NULL) {

            currentLine = code[currentLineNum];

            char (**tokenized)[LINE_LENGTH_LIMIT];

            for (int i = 0; i < strlen(currentLine); i++) {

                tokenized[i] = strtok(currentLine, " ");

                if (tokenized[i] == NULL) 
                    break;
            }

            char *currentToken;

            for (int i = 0; i < LINE_LENGTH_LIMIT; i++) {

                currentToken = tokenized[i];

                if (strcmp("***", currentToken)) 
                    break;

                char (*nextToken) = tokenized[i + 1];

                if (strcmp("global", currentToken)) {

                    if (!strcmp("character", nextToken) && !strcmp("integer", nextToken) && !strcmp("double", nextToken) && !strcmp("string", nextToken)) {
                        printf("Declarations: unknown data type %s at line %d", nextToken, currentLineNum);
                    }

                }

                if (strcmp("character", currentToken) || strcmp("integer", currentToken) || strcmp("double", currentToken) || strcmp("string", currentToken)) {

                    char *functionName = strtok(nextToken, '(');

                    if (strcmp("character", functionName) || strcmp("integer", functionName) || strcmp("double", functionName) || strcmp("string", functionName) || strcmp("while", functionName) || strcmp("if", functionName) || strcmp("else", functionName) || strcmp("global", functionName) || strcmp("equal", functionName) || strcmp("nequal", functionName) || strcmp("return", functionName)) {

                        printf("Declarations: naming violation of %s at line %d", functionName, currentLineNum);
                    }

                    for (int i = 0; i < strlen(functionName); i++) {

                        if (!isalnum(functionName[i]) && (functionName[i] != '_') && (functionName[i] != '?')) {

                            printf("Declarations: naming violation of %s at line %d", functionName, currentLineNum);

                        }
                    }

                }
            }

        }
    }
}

#endif

I got several compiling warnings, I will add the warnings to the end, and when I try to launch the program, it immediately gives a "the program crashed" error, but I think it may be because of the not yet written header files. What can I do to get rid of the errors I got? Thank you for the answers, any help will be greatly appreciated. (Note that I'm new to C and I did not quite grasp the notion of interchangability between arrays and pointers and double pointers (e.g. : **ptr) )

...\declarationschecker.h(30): warning C4018: '<': signed/unsigned mismatch
...\declarationschecker.h(32): warning C4047: '=': 'char (*)[200]' differs in levels of indirection from 'char *'
...\declarationschecker.h(42): warning C4047: '=': 'char *' differs in levels of indirection from 'char (*)[200]'
...\declarationschecker.h(59): warning C4047: 'function': 'const char *' differs in levels of indirection from 'int'
...\declarationschecker.h(59): warning C4024: 'strtok': different types for formal and actual parameter 2
...\declarationschecker.h(66): warning C4018: '<': signed/unsigned mismatch
...\declarationschecker.h(47): warning C4047: 'initializing': 'char *' differs in levels of indirection from 'char (*)[200]'

The main c file which needs the headers is posted below:

#include "CodeReader.h"
#include "BracketsChecker.h"
#include "DeclarationsChecker.h"
#include "StatementsChecker.h"
#include "SSAChecker.h"

int main(int argc, char * argv[]) {
    if (argc < 2) {
        printf("Please provide X code file name\n");
        exit(1);
    }

    char **code = readCode(argv[1]);
    int  num_lines = getCodeNumLines();

    checkBrackets(code, num_lines);
    checkDeclarations(code, num_lines);
    checkProgramStatements(code, num_lines);
    checkSSA(code, num_lines);

    cleanMemory(code);

    int terminalHung; scanf("%d", &terminalHung);
    return 0;
}

Solution

  • First, without having access to the rest of your project (I assume there are several other files containing some of the functions you reference in your post), it is not possible to know how exactly you are creating your code generator, limiting suggestions to syntactical issues.

    Explanation of following errors from your post is in both comments below, and in-line comments in code at bottom: enter image description here

    First, In your edited section, I cannot see what the function readCode() is because you did not include it, but if it does not create memory then the variable code cannot be used.

    After declaring char (**tokenized)[LINE_LENGTH_LIMIT]; you try to use the zeroth array element of a char ** without first creating memory. At best, your program would crash during run-time, even worse, it would appear to work. This is referred to as Undefined, or Unspecified behavior. ( read up on how to use malloc ). Because you are preparing storage for a collection of strings, you will only need two diminsions, not three. Either a char *[] or a char ** would work. In either case, these must be initialized and memory created before use. However, since you already know max number of lines and max length of lines simply declare and use: char tokenized[CODE_LINE_LIMIT][LINE_LENGTH_LIMIT];.

    Also, declare char *token = 0; to use with strtok. (see comments for reason)

    Also, declare multiply used variables once (such as i. see comments for reason)

    For the rest, again, review in-line comments to see how the previous errors/warning have been addressed in your code:

    static void checkDeclarations(char **code, int num_lines) 
    {
    
        char *token = 0;//use with strtok
        char *currentLine;
        char tokenized[CODE_LINE_LIMIT][LINE_LENGTH_LIMIT] = {{0}};
        int i, len;  // declare multiply used variables once
    
        for (int currentLineNum = 0; currentLineNum < num_lines; currentLineNum++) {
    
            if (code[currentLineNum] != NULL) {
    
                currentLine = code[currentLineNum];
    
                //char (*tokenized)[LINE_LENGTH_LIMIT] = {0};
                len = strlen(currentLine);
                for( i = 0; i< len; i++ ) // corrected
                //for (int i = 0; i < strlen(currentLine); i++)  // don't do string comparison's in a loop
                {                                              // and avoid comparisons of different types
                                                               // return of strlen() is an unsigned int
                    token =  strtok(currentLine, " ");
    
                    if (token == NULL) break;
                    else strcpy(tokenized[i], token);
                }
    
                char *currentToken;
    
                //for (int i = 0; i < LINE_LENGTH_LIMIT; i++) {  // shadow declaration of previously declared variable
                for ( i = 0; i < LINE_LENGTH_LIMIT; i++) {       // corrected
    
                    currentToken = tokenized[i];
    
                    if (strcmp("***", currentToken)) 
                        break;
    
                    char (*nextToken) = tokenized[i + 1];
    
                    if (strcmp("global", currentToken)) {
    
                        if (!strcmp("character", nextToken) && !strcmp("integer", nextToken) && !strcmp("double", nextToken) && !strcmp("string", nextToken)) {
                            printf("Declarations: unknown data type %s at line %d", nextToken, currentLineNum);
                        }
    
                    }
    
                    if (strcmp("character", currentToken) || strcmp("integer", currentToken) || strcmp("double", currentToken) || strcmp("string", currentToken)) {
    
                        //char *functionName = strtok(nextToken, '(');  // strtok 2nd argument requires a string, not an integer
                        char *functionName = strtok(nextToken, "(");  // corrected
                    // note: calling this in a loop will be a problem.  either Declare 'functionName' at top of function
                    // or use 'token', already declared
    
                        if (strcmp("character", functionName) || strcmp("integer", functionName) || strcmp("double", functionName) || strcmp("string", functionName) || strcmp("while", functionName) || strcmp("if", functionName) || strcmp("else", functionName) || strcmp("global", functionName) || strcmp("equal", functionName) || strcmp("nequal", functionName) || strcmp("return", functionName)) {
    
                            printf("Declarations: naming violation of %s at line %d", functionName, currentLineNum);
                        }
    
                        //for (int i = 0; i < strlen(functionName); i++) { // "i" has already been declared above
                        for ( i = 0; i < len; i++) { // corrected
    
                            if (!isalnum(functionName[i]) && (functionName[i] != '_') && (functionName[i] != '?')) {
    
                                printf("Declarations: naming violation of %s at line %d", functionName, currentLineNum);
    
                            }
                        }
    
                    }
                }
    
            }
        }
    }
    

    Edit to address question in comment...
    The following may already be known to you, but your post gives no indication, so I am offering the following just in case:

    Before assigning a string to a char *str (eg. via use of strcpy or strcat, etc.) you must create memory:

    int desiredStrLen  = 80;
    char *str = calloc(desiredStrLen + 1, 1);
    if(str)// test return of calloc before trusting it worked
    {
        //use str 
        ...
        free(str); // always, when finished with any dynamically allocated memory, free it.  
    

    For a collection of strings (eg. required when reading lines of a file) you can create memory for a set of strings. Once determining the number of lines as well as the longest line in a file, you can create memory sufficient to copy each line read from the file into a string:

    char **currentLine = Create2DStr(numLines, longestLine);
    if(strings)
    {
        /// use currentLine (in your loop)
        ...
        strcpy(currentLine[i], code[currentLineNum]);
        ...
        // when finished with string collection, free it.
        free2DStr(&strings, numLines);  
    

    The functions I am using above can be implemented in many ways. I use the following:

    char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
    {
        int i;
        char **str = {0};
        str = calloc(numStrings, sizeof(char *));
        for(i=0;i<numStrings; i++)
        {
          str[i] = calloc(maxStrLen + 1, 1);
        }
        return str;
    }
    
    void free2DStr(char *** str, ssize_t numStrings)
    {
        int i;
        if(!(*str)) return;
        for(i=0;i<numStrings; i++)
        {
                free((*str)[i]);
                (*str)[i] = NULL;
        }
        free((*str));
        (*str) = NULL;
    }