How to convert logic to pointer logic - c

How to covert logic to pointer logic, so below i have it the way I understand to code, but I need this to be using pointer like it says in the TODO, please advise, TY :)
This is what is said in the TODO that is not helping me understand, (Hint: to avoid doing double pointer arithmatic, save a char* pointer
// to the active chunk[?] in the outer loop but before the inner loop.
// Then the inner loop is only concerned with a single array of
// characters rather than an array of strings.)
Loop over each string in the array of
// message chunks (strings) and print it. Do not print the first
// character in each string since it is only used to store the order of
// the chunks. (Hint: use pointer arithmetic to skip a character.)
////////////////////////////////////////////////////////////////////////////////
//INCLUDES
#include <stdio.h>
#include <string.h>
//macros: constants
#define CHUNK_LENGTH (20+1) //each chunk has twenty characters, we add 1 so
//there is space for the null terminator.
#define NUMBER_OF_CHUNKS 4 //the message is spread across 4 chunks.
#define DECRYPTION_SHIFT 5 //this is the ASCII table shift used for decryption.
//forward declarations
void sort_chunks();
void decrypt_chunks();
void display_chunks();
char chunks[NUMBER_OF_CHUNKS][CHUNK_LENGTH];
int main() {
//copy message into memory.
strcpy(chunks[0], "2i1%fsi%fs%jstwrtzx%");
strcpy(chunks[1], "1'H%nx%vznwp~1%kqf|j");
strcpy(chunks[2], "4R3%Wnyhmnj%%%%%%%%%");
strcpy(chunks[3], "3xzhhjxx3'%2%Ijssnx%");
//the format of a chunk is a single number indicating its order in overall
//message followed by twenty encrypted characters.
//reorder chunks in message by sorting them based on the first digital
//they contain. looking above, one can see they are currently in the order
//2, 1, 4, 3 but should be ordered 1, 2, 3, 4.
sort_chunks();
//shift the characters in the message to produce the original characters.
decrypt_chunks();
//display the decrypted message.
display_chunks();
return 0;
}
//given two strings, swaps their contents in memory.
void swap_strings(char* x, char* y) {
//create a temporary holding place for the data so we don't lose it.
char temp[CHUNK_LENGTH];
strcpy(temp, x);
strcpy(x, y);
strcpy(y, temp);
}
//sorts the strings the global chunks variable by the first character they contain.
void sort_chunks() {
//TODO: Implement sort_chunks(). Using your favorite sorting algorithm (we
// suggest selection sort), sort the array containing the message chunks.
// Sort based on the first character in the chunk - it will always be a
// number. We provide a swap_strings function that you may use. Example
// usage: swap_strings(chunks[0], chunks[1]) will swap the contents of
// the first and second string.
int i, j;
int lowest;
for (i = 0; i < NUM_OF_CHUNKS - 1; i++)
{
lowest = i;
for (j = i + 1; j < NUM_OF_CHUNKS; j++)
{
if (chunks[j][0] < chunks[lowest][0])
lowest = j;
}
swap_strings(chunks[i], chunks[lowest]);
}
}
//for each string in the global chunks variable, shifts the characters in it by
//DECRYPTION_SHIFT.
void decrypt_chunks() {
//TODO: Implement decrypt_chunks(). Loop over each string in the array
// and shift the characters in it by subtracting DECRYPTION_SHIFT value
// from them. Use pointer arithmetic to access individual characters but
// array access to the strings. Remember that C-style strings have a null
// terminator at the end. Do not apply the shift to the terminator.
// (Hint: to avoid doing double pointer arithmetic, save a char* pointer
// to the active chunk[?] in the outer loop but before the inner loop.
// Then the inner loop is only concerned with a single array of
// characters rather than an array of strings.)
int row, col;
for (row = 0; row < NUM_OF_CHUNKS; row++)
{
for (col = 0; col < CHUNK_LENGTH - 1; col++)
{
if (chunks[row][col] != '\0')
chunks[row][col] -= DECRYPTION_SHIFT;
}
}
}
//displays the strings in the global chunks variable
void display_chunks() {
//TODO: Implement display_chunks(). Loop over each string in the array of
// message chunks (strings) and print it. Do not print the first
// character in each string since it is only used to store the order of
// the chunks. (Hint: use pointer arithmetic to skip a character.)
int row, col;
for (row = 0; row < NUM_OF_CHUNKS; row++)
{
for (col = 1; col < CHUNK_LENGTH - 1; col++)
{
if (chunks[row][col] == '\0')
{
printf("\n");
return;
}
printf("%c", chunks[row][col]);
}
}
printf("\n");
}

Let's make this instructive. Let's use a simple preprocessor define WPOINTERS to allow your code to be compiled to use pointer or use the two loops in your original code:
//displays the strings in the global chunks variable
void display_chunks() {
//TODO: Implement display_chunks(). Loop over each string in the array of
// message chunks (strings) and print it. Do not print the first
// character in each string since it is only used to store the order of
// the chunks. (Hint: use pointer arithmetic to skip a character.)
int row;
for (row = 0; row < NUMBER_OF_CHUNKS; row++)
{
#ifdef WPOINTERS
char *p = chunks[row];
printf ("%s", p + 1);
#else
int col;
for (col = 1; col < CHUNK_LENGTH - 1; col++)
{
if (chunks[row][col] == '\0')
{
putchar ('\n');
return;
}
putchar (chunks[row][col]);
}
#endif
}
putchar ('\n');
}
If you specify -DWPOINTERS on the command line during compile, the following code is executed:
#ifdef WPOINTERS
char *p = chunks[row];
printf ("%s", p + 1);
otherwise, your original code is executed:
#else
int col;
for (col = 1; col < CHUNK_LENGTH - 1; col++)
{
if (chunks[row][col] == '\0')
{
putchar ('\n');
return;
}
putchar (chunks[row][col]);
}
In both cases you obtain:
$ ./bin/prnwptrsnew
"C is quirky, flawed, and an enormous success." - Dennis M. Ritchie
Example compile strings would be:
$ gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast -DWPOINTERS -o bin/prnwptrsnew prnwptrs.c
or for your original code, just omit -DWPOINTERS:
$ gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast -o bin/prnwptrsnew prnwptrs.c
If you are using cl.exe, the with/without -DWPOINTERS works the same.

Related

How to reduce the time complexity of seaching the same character in two different strings in C?

Input contains a
n
which indicated the total amount of the strings.
Then use scanf to scan those strings.
The task is that find out if two strings have the same characters.
If so,they are in the same group.
Two string belongs to same group if :
1.there exists a character that exist in both string.
2.there exists a character in both string A and B,
and there exists another character in both string B and C,
then A, B, C belong to same group.
for example
>>input
5
abbbb
a
c
ddca
fgg
Here "abbbb","a","c","ddca" are in the same group
and output the total numbers of groups
in this example is
2
Every characters in the strings only contains 'a'~'z'
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct str //'ini' is used to save the initial strings 'convert' recorded the characters converted to ascii code then -'a'
{
char ini[1001];
int convert[1001];
} STR;
STR a[2002];
int visited[2002];// record the index that has been traverse
int count;//record the total number of groups
int record[26];//there is 26 characters from'a'~'z'
int check(int index1,int index2)
{
int len_1=strlen(a[index1].ini);
int len_2=strlen(a[index2].ini);
for(int i=0; i<26; i++)//reset
{
record[i]=0;
}
for(int i=0; i<len_1; i++)//traverse the first string and recorded the characters in "record"array
{
record[a[index1].convert[i]]=1;
}
for(int i=0; i<len_2; i++)
{
if(record[a[index2].convert[i]]==1)//if they have same characters
{
return 1;
}
}
return 0;
}
void dfs(int now,int n)//now record index ,n record the total index
{
visited[now]=1;
for(int i=0; i<n; i++)
{
if(i==now) continue;
if(visited[i]==1) continue;
if(check(i,now))
{
dfs(i,n);
}
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0; i<n; i++)
{
scanf("%s",a[i].ini);
int len=strlen(a[i].ini);//recording the length of the input string
for(int j=0; j<len; j++) //convert every characters in the ini string to ascii then -'a', recording in 'convert' int array
{
char ch=a[i].ini[j];
a[i].convert[j]=ch-'a';
}
}
for(int i=0; i<n; i++)//dfs
{
if(visited[i]==0)
{
count++;
dfs(i,n);
}
}
printf("%d\n",count);
return 0;
}
I tried to use DFS to search it, and use every strings as a node
Is that any better way to reduce the time complexity?
Can it be more faster?
You iterate over each string 2 times (strlen + the for loop). That's rather inefficient.
When done looking through the string(s), there shouldn't be a need to search for anything, if you place the results in a sensible way.
Ideally keep track of the results from each string separately.
Something like this (naive implementation):
#define LETTERS 26
void str_common (const char* s1, const char* s2)
{
bool record1 [LETTERS]={false};
bool record2 [LETTERS]={false};
for(size_t i=0; s1[i]!='\0'; i++)
{
record1[s1[i]-'a']=true;
}
for(size_t i=0; s2[i]!='\0'; i++)
{
record2[s2[i]-'a']=true;
}
for(size_t i=0; i<LETTERS; i++)
{
if(record1[i] && record2[i])
{
putchar('a' + i);
}
}
}
This is naive code because it has no error handling and also assumes that all letters in the symbol table are adjacent, which C makes no guarantees for. Still it gives something to start with.
You can rewrite this function to only work with one string at a time and instead of printing, returning the record array to the caller. Comparing which letters that exists in your 3 different strings then becomes trivial, just change the if(record1[i] && record2[i]) check to contain as many strings and conditions as you need.
There is a place for some optimization. First, drop the convert arrays and replace it with bool usedLetters[256];. Fill it after reading each string with a simple loop over the string:
scanf("%s",a[i].ini);
for(int j=0; a[i].ini[j] != 0; j++) //scan the string till its end
{
unsigned char ch=a[i].ini[j]; // get a next character
a[i].usedLetters[ch] = true; // and note it's used
}
Then checking whether two strings share a letter requires a parallel scanning of both corresponding usedLetters[]:
int check(int index1,int index2)
{
bool *used1 = a[index1].usedLetters;
bool *used2 = a[index2].usedLetters;
for(int i=0; i < 256; i++) // iterate over all possible chars
{
if(used1[i] && used2[i]) // char(i) used in both strings?
return 1;
}
return 0;
}
However, the main cost is in your scanning routine. It's not DFS (unless you consider the whole set of strings as a full graph) – for each string visited you try to compare it to all strings in a set, then discarding those already visited. As a result, your routine performs up to N^2 iterations of a loop and (in a worst case of all strings disjoint) up to ~(N^2)/2 calls to check() (it will need to identify N one-string 'groups', each time checking a starting string against N/2 other strings on average).

HEAP CORRUPTION DETECTED: after normal block(#87)

I'm trying to do a program that get number of names from the user, then it get the names from the user and save them in array in strings. After it, it sort the names in the array by abc and then print the names ordered. The program work good, but the problem is when I try to free the dynamic memory I defined.
Here is the code:
#include <stdio.h>
#include <string.h>
#define STR_LEN 51
void myFgets(char str[], int n);
void sortString(char** arr, int numberOfStrings);
int main(void)
{
int i = 0, numberOfFriends = 0, sizeOfMemory = 0;
char name[STR_LEN] = { 0 };
char** arrOfNames = (char*)malloc(sizeof(int) * sizeOfMemory);
printf("Enter number of friends: ");
scanf("%d", &numberOfFriends);
getchar();
for (i = 0; i < numberOfFriends; i++) // In this loop we save the names into the array.
{
printf("Enter name of friend %d: ", i + 1);
myFgets(name, STR_LEN); // Get the name from the user.
sizeOfMemory += 1;
arrOfNames = (char*)realloc(arrOfNames, sizeof(int) * sizeOfMemory); // Change the size of the memory to more place to pointer from the last time.
arrOfNames[i] = (char*)malloc(sizeof(char) * strlen(name) + 1); // Set dynamic size to the name.
*(arrOfNames[i]) = '\0'; // We remove the string in the currnet name.
strncat(arrOfNames[i], name, strlen(name) + 1); // Then, we save the name of the user into the string.
}
sortString(arrOfNames, numberOfFriends); // We use this function to sort the array.
for (i = 0; i < numberOfFriends; i++)
{
printf("Friend %d: %s\n", i + 1, arrOfNames[i]);
}
for (i = 0; i < numberOfFriends; i++)
{
free(arrOfNames[i]);
}
free(arrOfNames);
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char str[], int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
/*In this function we get array of strings and sort the array by abc.
Input: The array and the long.
Output: None*/
void sortString(char** arr, int numberOfStrings)
{
int i = 0, x = 0;
char tmp[STR_LEN] = { 0 };
for (i = 0; i < numberOfStrings; i++) // In this loop we run on all the indexes of the array. From the first string to the last.
{
for (x = i + 1; x < numberOfStrings; x++) // In this loop we run on the next indexes and check if is there smaller string than the currnet.
{
if (strcmp(arr[i], arr[x]) > 0) // If the original string is bigger than the currnet string.
{
strncat(tmp, arr[i], strlen(arr[i])); // Save the original string to temp string.
// Switch between the orginal to the smaller string.
arr[i][0] = '\0';
strncat(arr[i], arr[x], strlen(arr[x]));
arr[x][0] = '\0';
strncat(arr[x], tmp, strlen(tmp));
tmp[0] = '\0';
}
}
}
}
After the print of the names, when I want to free the names and the array, in the first try to free, I get an error of: "HEAP CORRUPTION DETECTED: after normal block(#87)". By the way, I get this error only when I enter 4 or more players. If I enter 3 or less players, the program work properly.
Why does that happen and what I should do to fix it?
First of all remove the unnecessary (and partly wrong) casts of the return value of malloc and realloc. In other words: replace (char*)malloc(... with malloc(..., and the same for realloc.
Then there is a big problem here: realloc(arrOfNames, sizeof(int) * sizeOfMemory) : you want to allocate an array of pointers not an array of int and the size of a pointer may or may not be the same as the size of an int. You need sizeof(char**) or rather the less error prone sizeof(*arrOfNames) here.
Furthermore this in too convoluted (but not actually wrong):
*(arrOfNames[i]) = '\0';
strncat(arrOfNames[i], name, strlen(name) + 1);
instead you can simply use this:
strcpy(arrOfNames[i], name);
Same thing in the sort function.
Keep your code simple.
But actually there are more problems in your sort function. You naively swap the contents of the strings (which by the way is inefficient), but the real problem is that if you copy a longer string, say "Walter" into a shorter one, say "Joe", you'll write beyond the end of the allocated memory for "Joe".
Instead of swapping the content of the strings just swap the pointers.
I suggest you take a pencil and a piece of paper and draw the pointers and the memory they point to.

Remove some elements from array and re-size array in C

Regards
I want to remove some elements from my array and re-size it.
for example my array is:
char get_res[6] = {0x32,0x32,0x34,0x16,0x00,0x00};
Now I want to remove elements after 0x16, so my desire array is:
get_res[] = {0x32,0x32,0x34,0x16};
what is solution?
You cannot resize arrays in C (unlike Python, for example). For real resizing, at least from an API user's point of view, use malloc, calloc, realloc, and free (realloc specifically).
Anyway, "resizing" an array can be imitated using
a delimiter; for example, a delimiter like 0xff could mark the end of the valid data in the array
Example:
#define DELIMITER 0xff
print_data(char* data) {
for (size_t i = 0; data[i] != DELIMITER; ++i)
printf("%x", data[i]);
}
a member counter; count the number of valid data from the beginning of the array onward
Example:
size_t counter = 5;
print_data(char* data) {
for (size_t i = 0; i < counter; ++i)
printf("%x", data[i]);
}
Notes:
Use unsigned char for binary data. char may be aliasing signed char, which you might run into problems with because signed char contains a sign bit.
There is no need to "remove" them. Just don't access them. Pretend like they don't exist. Same like in stacks, when you "pop" a value from the top of the stack, you just decrement the stack pointer.
Manipulating arrays in C isn't easy as it is for vector in C++ or List in Java. There is no "remove element" in C. I mean that you have to do the job yourself, that is, create another array, copy only the elements you want to this new array, and free the memory occupied by the previous one.
Can you do that? Do you want the code?
EDIT:
Try that. It's just a simple program that simulates the situation. Now, you have to see the example and adapt it to your code.
#include <stdio.h>
#include <stdlib.h>
int main() {
char get_res[6] = {0x32,0x32,0x34,0x16,0x00,0x00};
char target = 0x16;
int pos, i, length = 6; // or specify some way to get this number
for(i = 0; i < length; i++)
if(get_res[i] == target) {
pos = i;
break;
}
pos = pos + 1; // as you have to ignore the target itself
char *new_arr = malloc(pos);
for(i = 0; i < length; i++) {
new_arr[i] = get_res[i];
i++;
}
for(i = 0; i < pos; i++)
printf("%c ", new_arr[i]);
return 0;
}

Concat LPSTR in C

Trying to use as basic C as I can to build a list of numbers from 1-52 in a random order (deck of cards). Everything works, but all of my attempts to concat the strings and get a result end in failure. Any suggestions? NOTE: This is not homework it's something I'm using to create a game.
// Locals
char result[200] = ""; // Result
int card[52]; // Array of cards
srand(time(0)); // Initialize seed "randomly"
// Build
for (int i=0; i<52; i++) {
card[i] = i; // fill the array in order
}
// Shuffle cards
for (int i=0; i<(52-1); i++) {
int r = i + (rand() % (52-i));
int temp = card[i]; card[i] = card[r]; card[r] = temp;
}
// Build result
for (int c=0; c<52; c++) {
// Build
sprintf(result, "%s%d", result, card[c]);
// Comma?
if ( c < 51 )
{
sprintf(result, "%s%s", result, ",");
}
}
My end result is always garbled text. Thanks for the help.
You keep writing to the same position of "result".
sprintf is not going to do the appending for you.
You may consider, after each sprintf, get the return value (which is the number of char written), and increment the pointer to result buffer. i.e. something like:
(psuedo code):
char result[200];
char * outputPtr = result;
for (int c=0; c<52; c++) {
// Build
int n = sprintf(outputPtr, "%d%s", card[c], (c<51 ? "," : ""));
outputPtr += n;
}
Are we writing C++ or C? In C++, concat-ing a string is just:
string_out = string_a + string_b
…since you'd be using std::string.
Furthermore, if this is C++, the STL has a std::shuffle function.
If this is C, note that all your sprintfs aren't concatenating strings, they're just overwriting the old value.
I think, if memory serves, that sprintf will always write into the buffer starting at byte 0. This means that you would be writing the first couple of bytes over and over again with a number, then a comma, then a number. Check if your first bytes are ",[0-9]" - if so, that's your issue.
This would add a comma between each number in the result string:
// Get a pointer to the result string
char* ptr = &result[0];
for (int c = 0; c < 52; c++) {
// Add each cards number and increment the pointer to next position
ptr += sprintf(ptr, "%d", card[c]);
// Add a separator between each number
if (c < 51) {
*ptr++ = ',';
}
}
// Make sure the result string is null-terminated
*ptr = 0;

How would you add chars to an array dynamically? Without the array being predefined?

If I want to add chars to char array, I must do it like this:
#include <stdio.h>
int main() {
int i;
char characters[7] = "0000000";
for (i = 0; i < 7; i++) {
characters[i] = (char)('a' + i);
if (i > 2) {
break;
}
}
for (i = 0; i < 7; i++) {
printf("%c\n", characters[i]);
}
return 0;
}
To prevent from printing any weird characters, I must initialize the array, but it isn't flexible. How can I add characters to a char array dynamically? Just like you would in Python:
characters = []
characters.append(1)
...
There is no non-ugly solution for pure C.
#include <stdio.h>
int main() {
int i;
size_t space = 1; // initial room for string
char* characters = malloc(space); // allocate
for (i = 0; i < 7; i++) {
characters[i] = (char)('a' + i);
space++; // increment needed space by 1
characters = realloc(characters, space); // allocate new space
if (i > 2) {
break;
}
}
for (i = 0; i < 7; i++) {
printf("%c\n", characters[i]);
}
return 0;
}
In practice you want to avoid the use of realloc and of course allocate the memory in bigger chunks than just one byte, maybe even at an exponetial rate. But in essence thats what happening under the hood of std::string and the like: You need a counter, which counts the current size, a variable of the current maximum size (Here it is always current size+1, for simplicity) and some reallocation if the need for space surpasses the maximum current size.
Yes, of course you can add characters dynamically:
quote char[100] = "The course of true love";
strcat( quote, " never did run smooth.";
but only if there is enough room in quote[ ] to hold the appended characters. Or maybe you are asking why, in C, you have to pre-arrange enough character storage whereas, in Python, storage is allocated dynamically. That's how the language was designed in 197x.
C99 does allow dynamically-allocated storage: storage allocated by the system at run time. And a very bad mistake it is, imo.
You cannot unless you use Linked Lists or some other custom data structure.

Resources