-4

My program works fine when i use a 81 elements array but when i deal with a 256 blocks array it crashes and i dont know why it does that. I present to you the all source code and in the input text that you can use in a .txt file with a last carriage return.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <malloc.h>

int grille[256];
int domaine[256][17];     /* indique les valeurs possib pour case donnee */
int domaine_taille[256];     /* nombre de valeurs possibles une case donnee */
int compteur_noeuds;
void restreindre_domaines(int);
int backtrack(int imite_limite_par_ton_nez);

#define PERR(bSuccess, api) \
  {if (!(bSuccess)) perr(__FILE__, __LINE__,api, GetLastError());}
#define INCONNU -1

void lire_grille()
  {
  int k = 0;

  while (k < 256) { 
    char c = getchar();
    if (c >= '0' && c <= '9' )  //0 a 9
      grille[k++] = c-48;
    if(c >= 'a' && c <= 'f')    // a a f
      grille[k++] = c-87;
    if (c == '.' || c == ' ' || c== '+') // espace  +  ou   .
      grille[k++] = INCONNU;
    if (c == EOF)
      {
      fprintf(stderr, "Lecture de la grille : mauvais format\n");
      exit(1);
  }  }}

void afficher_grille()   /* afficher la grille courante sur stdout */
  {
  int c, l;

  for (l = 0; l < 16; l++) {
    for (c = 0; c < 16; c++) {
      int k = l*16+c;
      if (grille[k] == INCONNU) printf(".");
      if (grille[k] >= 0 && grille[k] <= 9 )  //0 a 9
        printf("%c", grille[k]+48);
      if(grille[k] >= 10 && grille[k] <= 15)    // a a f
        printf("%c", grille[k]+87);  
      }
    printf("\n");
    }
  printf("\n");
  }



void init_domaines()  /* en fonction grille,initialiser domaines */
  {
  int i, v;

  for (i = 0; i < 256; i++)
    {
    domaine_taille[i] = 16;
    for (v = 0; v <= 16; v++)
      domaine[i][v] = 1;
    }
  for (i = 0; i < 256; i++) /* restreindre domaines cases remplies */
    if (grille[i] != INCONNU)   restreindre_domaines(i);
  }



/*oter valeurs des domaines incompatibles avec valeur case i */
void restreindre_domaines(int i)
  {
  int l = i / 16;    //ligne
  int c = i % 16;   //colonne
  int lb = l / 4;   //
  int cb = c / 4;   //
  int l2, c2, lr2, cr2, i2;

  /* contraintes de la colonne contenant la case i */
  for (l2 = 0; l2 < 16; l2++) {
    i2 = l2*16+c;
    if (domaine[i2][grille[i]]!=0) {
      domaine[i2][grille[i]] = 0;
      domaine_taille[i2]--;
      }
    }

  /* contraintes de la ligne contenant la case i */
  for (c2 = 0; c2 < 16; c2++) {
    i2 = l*16+c2;
    if (domaine[i2][grille[i]]) {
      domaine[i2][grille[i]] = 0;
      domaine_taille[i2]--;
      }
    }

  /* verifier la region contenant la case i */
  for (lr2 = 0; lr2 < 4; lr2++)
    for (cr2 = 0; cr2 < 4; cr2++) {
      i2 = (lb*4+lr2)*16+(cb*4+cr2);
      if (domaine[i2][grille[i]]) {
        domaine[i2][grille[i]] = 0;
        domaine_taille[i2]--;
  }}}

int main()
  {
  lire_grille();
  afficher_grille();
  init_domaines();
  backtrack(0);
  printf("%d noeuds cherches\n", compteur_noeuds);

  printf(" \n");
  _getch();
  return 0;
  }

int backtrack(int uz)
  {
  if (uz==-99) return(uz);
  int i, i_min = -1,aa=0;
  int taille_min = 17;
  int domaine2[256][17];
  int domaine_taille2[256];
  static int toc,Toc;
  int iz,zzz=0;
  toc++;
  static int xu;
  xu=true;

  int lnum[16];

  lnum[0]=0;
  lnum[1]=15;
  lnum[2]=14;
  lnum[3]=13;
  lnum[4]=12;
  lnum[5]=11;
  lnum[6]=4;
  lnum[7]=3;
  lnum[8]=2;
  lnum[9]=1;
  lnum[10]=5;
  lnum[11]=10;
  lnum[12]=9;
  lnum[13]=8;
  lnum[14]=7;
  lnum[15]=6;

  int tst=0;
  for (iz = 0; iz < 256; iz++)
    {
    if (grille[iz] != INCONNU)tst++;
    }
  if(tst==256) 
    {   /*fin:*/
    // printf("\n%d  2  valeur tst\n", tst);
    afficher_grille();
    xu=0;
    return xu;
    }

  /* chercher la case avec le domaine le plus petit */
  for (i = 0; i < 256; i++)
    {
    if (grille[i] == INCONNU && domaine_taille[i] < taille_min)
      {
      i_min = i;
      taille_min = domaine_taille[i];
      //printf("i %d *do_tail[i] %d *tail_min %d \n",i,domaine_taille[i],taille_min);
      }

    if( toc >10000)
      {
      toc=0;
      afficher_grille();
      printf("\n%d noeuds cherches\n", compteur_noeuds);
      printf(" toc %d et Toc %d  \n",toc,Toc);
      Toc++;
      printf("-------------------------------------------\n");
      }
    }

  compteur_noeuds++;

  //    for (grille[i_min] = lnum[aa]; grille[i_min] <= lnum[aa+15]; grille[i_min]++,aa++)
  for (grille[i_min] = 0; grille[i_min] <= 15; grille[i_min]++,aa++)
    {
    if (domaine[i_min][grille[i_min]] == 0)
      continue;
      /* on sauvegarde l'etat courant de domaine
    et domaine_taille pour les retablir apres l'appel recursif */
    memcpy (domaine2, domaine, sizeof(domaine));
    memcpy (domaine_taille2, domaine_taille, sizeof(domaine_taille));
    restreindre_domaines(i_min);
    backtrack(uz);
    if (xu==0)return xu;
    memcpy (domaine, domaine2, sizeof(domaine));
    memcpy (domaine_taille, domaine_taille2, sizeof(domaine_taille));
    }
  grille[i_min] = INCONNU;
  return 0;
  }

VadeMecum: prog.exe < inp.txt

here is a inp.txt text with a good format (16x16 1-F number and . for case to search)

.ae692...0..5.8d
5....4.12.37.bc6
..f.....5......4
.b.8e5f..9.d..01
.42b.17.3.......
.3.52....bf0...c
.......a.1..042b
....4.cb8...1..a
....f.4eb...3..8
.......5.3..acef
.7.2d....51f...9
.e3c.ab.0.......
.0.a58e..7.1..63
..1.....d......2
4....d.26.59.eb0
.6b7c9...4..d.15

So if you can help me .

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • 6
    Recommend reading this: http://stackoverflow.com/help/how-to-ask and this http://stackoverflow.com/help/mcve. Would also recommend trying to debug. Even worse you have some OS-specific includes in your program. If you want people to help you should definitely post a Minimal code that reproduces your errors. And variable names should be in english, not french ;) – LBes Oct 30 '15 at 14:46
  • 2
    [How to debug small programs](http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – m.s. Oct 30 '15 at 14:47
  • 5
    `backtrack` requires approximately 20KB of stack *per recursive call*. It's not obvious to me how deep the recursion goes, but if you're on Windows (as your code suggests) you only get 2MB of stack, and that's only enough for a recursion depth of 100 or so. Consider using `unsigned short`, or maybe even `unsigned char` if all the necessary values will fit, for all the arrays. If that isn't good enough you are going to have to get rid of the recursion and/or switch to heap allocated scratch arrays. – zwol Oct 30 '15 at 14:52
  • Which `81` which `256` ? – Jabberwocky Oct 30 '15 at 14:56
  • 1
    Simply don't use recursion and all your problems will go away. There exists very few valid uses of recursion and this is not one of them. – Lundin Oct 30 '15 at 15:00
  • BTW: all those `memcpy`s in `backtrack` look suspicious to me, you probably shouldn't need them by reviewing your algorithm. BTW what is your program supposed to do ? – Jabberwocky Oct 30 '15 at 15:06
  • 1
    @Lundin I don't think that's useful advice for someone at the OP's level of understanding. Transforming a recursive algorithm into an iterative algorithm is often quite complicated, and they may not have gotten to those lessons yet. – zwol Oct 30 '15 at 15:08
  • Since the header file: `conio.h` is not portable, strongly suggest not using the functions it exposes. – user3629249 Oct 30 '15 at 16:09
  • for readability/understandability of the code, 1) indent consistently, suggest (for several reasons) 4 spaces per indent level. Indent after every opening brace '{' and un-indent before every closing brace '}'. 2) declare only one variable per statement, only one statement per line 3) use meaningful variable names. You may have to type a bit more characters, but it is well worth it. – user3629249 Oct 30 '15 at 16:15
  • The posted code is missing the header file: `stdbool.h` so `true` and `false` are not defined – user3629249 Oct 30 '15 at 16:16
  • When compiling, always enable all the warnings (for gcc, at a minimum use: `-Wall -Wextra -pedantic` ) then fix those warnings. – user3629249 Oct 30 '15 at 16:18
  • For readability by us humans, a bit of spacing, such as separating code blocks (for, if, else, while, do...while, etc) by a blank line and a horizontal space between parameters, etc would go al long ways – user3629249 Oct 30 '15 at 16:20
  • the code contains many `magic` numbers. `magic` numbers make the code more difficult to understand and are a major headache when debugging and/or maintaining the code. Suggest using #defines or an enum to give those numbers meaningful names, then using those meaningful names throughout the code – user3629249 Oct 30 '15 at 16:22
  • the function: `lire_grille()` fails to handle capital letters and the local variable 'c' is defined as 'char', however getchar() returns an 'int' and EOF is an int. So this line: `char c = getchar();` should be: `int c = getchar();` No other references to 'c' need to be changed – user3629249 Oct 30 '15 at 16:28
  • @ Michael Walz : 81 is the size of the array of sudoku game(9x9) so this works and 256 is the size of non working fine version (16x16 elements in array). Thanks, but in fine i don't understand why the 81 lements works and why the big not. The stack perhaps, i will try to size the stack up. Thanks for your advises. – Chriistophe Oct 30 '15 at 16:33
  • such lines as: `if (c >= '0' && c <= '9' )` can be greatly simplified and much clear by adding: `#include ` then replace the line with: `if( isdigit(c) )`. similar simplifications/clarifications can be applied to the other if' statements in the `lire_grille()` function, You might also want to use the `tolower()` function on all the input characters to reduce the amount of testing needed, such as tests for capital characters, etc. – user3629249 Oct 30 '15 at 16:36
  • @ user3629249: i am readind yours comments, i am happy that someone gives to me advises. So i have understand for magic numbers i will care about that. About stdbool.h i dont like use boolean variables but for my next question i will be care that this file will be in my code. Thanks for your experience. – Chriistophe Oct 30 '15 at 16:38
  • you mention sudoku, which uses a 9x9 grid, no letters, no other values from the user, So please explain why the letters a...f, ., space, + are expected inputs from the user. – user3629249 Oct 30 '15 at 16:49
  • @user3629249 When `char` is signed and `c < 0`, `c >= '0' && c <= '9'` is well defined. `isdigit(c)` is undefined behavior. – chux - Reinstate Monica Oct 30 '15 at 16:49
  • the posted code uses the bool value `true`, so it is using boolean. So the header `stdbool.h` needs to be included. – user3629249 Oct 30 '15 at 16:51
  • [@user3629249](http://stackoverflow.com/users/3629249/user3629249) [Variations of grid sizes](https://en.wikipedia.org/wiki/Sudoku#Variations_of_grid_sizes) support 16x16 grids. – chux - Reinstate Monica Oct 30 '15 at 16:52
  • @chux, isdigit(c) returns true when 'c' is in the range 0x30 through 0x39 and false for all other values of 'c', irregardless if 'c' is an 'int', as it should be for `getchar()`, or 'unsigned int' – user3629249 Oct 30 '15 at 16:57
  • @user3629249 Concerning `isdigit()`, the C spec says it is defined for all `unsigned char` values and `EOF`, not that range of `char`, which is `c`. Agree `c` should be an `int`. `unsigned int` has its shortcoming too. Further, the spec does not specify the range 0x30 through 0x39, but `0` to `9` for portability reasons. – chux - Reinstate Monica Oct 30 '15 at 17:04
  • the stack size problem could be eliminated in the `backtrack()` function by using `malloc(`) to allocate the needed buffers and passing those buffers to `free()` when done with them. – user3629249 Oct 30 '15 at 17:08
  • @user3629249: No the reservation on stack space is not effcicient because the release goes after the last call in recursion. This system take the same space as the automatic reservation . (like in the program). I have the same problem. – Chriistophe Oct 30 '15 at 17:25
  • my man page for isdigit() says the syntax is: `int isdigit(int c);` I.E. a signed integer for a parameter. my man page for getchar() says the syntax is: `int getchar(void);` so it returns a signed integer, so the `isdigit()` function is very appropriate for the posted code to use. – user3629249 Oct 30 '15 at 17:29
  • @user3629249 except that OP used `char c`. Had OP used `int c`, then `isdigit(c)` would be more readable as you commented. But since OP user `char c`, `c >= '0' && c <= '9'` is well defined whereas your suggested visual improvement of `isdigit(c)` is not, - as originally [commented](http://stackoverflow.com/questions/33438687/why-does-my-program-crash-with-a-stack-overflow/33441613#comment54670206_33438687). – chux - Reinstate Monica Oct 30 '15 at 17:32
  • @chux, the getchar() function returns a int, the EOF value is an int. The comparison of equal of a char to 0xffffffff (=EOF) will always fail. Rather than try to justify an error in the code, simply fix it. – user3629249 Oct 31 '15 at 12:55
  • @user3629249,@chux,@all, if i want to push up the stack size just for my program ? can i do that ? and if yes how can i do that ? In the code a special constant or in the compil parameters or in linker parameters ? If you known tell me how i can do that because if i try the code with the **char** type (so 1 byte) the program has always the same comportement (it crashes with stack fault ...) – Chriistophe Oct 31 '15 at 14:29
  • 1) stack size control is not a part of C, but of your compiler. Review compiler options. 2) Clarify "try the code with the `char` type". What variables are you changing to `char`? – chux - Reinstate Monica Oct 31 '15 at 15:27
  • @user3629249 "comparison of equal of a char to 0xffffffff (=EOF) will always fail" is simply wrong when `char` has a range including negative values. What is the value of `CHAR_MIN` on your machine? On machines where `char` includes a negative range , quite common, `char c = CHAR_MIN; printf("%d", c);` will nicely print some negative number. OP's code was not necessarily in error, but with `char c` and `c` having a negative value that is not `EOF`, `isdigit(c)` is undefined behavior. – chux - Reinstate Monica Oct 31 '15 at 15:37
  • By default, in C, `char` is defined as 'unsigned' so when comparing a char 0xff to EOF 0xffffffff the char will be extended to 0x000000ff. Then the two will not match. – user3629249 Nov 01 '15 at 09:09
  • @user3629249,@chux; on my computer with limits header the C says that SCHAR_MAX is 128 (or 255 with -j unsigned option) and INT_MAX is 2147483647, but this difference is not significative for my memory problem. I looking for the stack options on mvc and gcc bridges options tout increase my link-memory stack. – Chriistophe Nov 01 '15 at 12:21
  • @user3629249 "By default, in C, `char `is defined as 'unsigned'" is inconsistent with the C spec. Perhaps the spec is wrong and you are correct? `char` is same as `signed char` or `unsigned char`, depending on the implementation. What is the value of `CHAR_MIN` on your machine? "If the value of an object of type `char` is treated as a signed integer when used in an expression, the value of `CHAR_MIN` shall be the same as that of `SCHAR_MIN` and the value of `CHAR_MAX` shall be the same as that of `SCHAR_MAX`. ..." C11 §5.2.4.2.1 2 – chux - Reinstate Monica Nov 01 '15 at 20:40
  • @Chriistophe Your comment "my computer with limits header the C says that `SCHAR_MAX` is 128". If that is true, it is a very unusual machine. Are you sure is wasn't 127? BTW, what is the value of `CHAR_MIN` on your machine? – chux - Reinstate Monica Nov 01 '15 at 20:45
  • @chux , i am wrong of course, i will say `SCHAR_MIN =CHAR_MIN= 127` and the mini on the other side for `-128 signed`, or `255 for non signeds` values. I am sorry. – Chriistophe Nov 02 '15 at 07:51
  • @zenith, NathanOliver, Chris Beck, George Stocker To change some things in a working programm you all are champions, but to try made working an unstackable program you are not very cute. When i see your remarks , it s seems that all of you are **Muppets**... don t you know ? You 're certainly very jalouse like are youngs ladies. – Chriistophe Nov 03 '15 at 13:36

1 Answers1

1

Recursion too deep and too expensive in space. @zwol

I had the same stack over-flow. Yet by changing int arrays to char, it was not too deep. Recurse depth of 251 resulted in

cae69237104b5f8d
590da4812f37ebc6
72f1bcd0568e9a34
3b48e5f6a9cd7201
642b017d3c9a85fe
13a52e984bf06d7c
8c9e3f5a71d6042b
df7046cb8e25139a
0159f74eb2ac36d8
bd6410259378acef
a782d36ce51fb049
fe3c8ab90d642157
20da58efc7b14963
951f6b04d8e3c7a2
48c37d126a59feb0
e6b7c9a3f402d815

17310 noeuds cherches

Should this reduction prove to be insufficient, allocate memory for the arrays or alter the algorithm.


The code has various other short-comings, covered in comments, that obfuscate the central issue. Fixing those do not solve the issue, but do make it easier for others (and likely OP) to diagnose.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256