Na początek CR.
1. W kodzie masz ostrzeżenia o arytmetyce signed/unsigned w 8 i 11 linii. liczbaTestow powinna być unsigned (tak jak iloscLiczb oraz indeksy i, j, k)
2. Dopuszczalna sygnatura main(...) w C to: int main(void).
3. W C jest zbędne rzutowanie wyniku malloc(...). To jest void * i zawsze poddaje się automatycznie konwersji na typ docelowy.
BTW. Zastanów się czy alokacja dynamiczna jest tu potrzebna jeśli masz tylko 100 elementów :)
4. Przy malloc(...) i innych podobnych funkcjach, stosuje się idiom z C który do argumentu sizeof, przyjmuje to co jest po lewej przypisania. Czyli nie tak:
int** tab = malloc(sizeof(int*)*liczbaTestow);
... tylko tak:
int** tab = malloc(sizeof(*tab) * liczbaTestow);
Dzięki temu będziesz mógł zmienić typ danych tab bez poprawiania ciała malloc(...)
5. Jak wyprowadzasz jeden znak, printf(..) w 33 zamień na putchar(...)
6. Preferuj pre (inc/dec) przed post (pre/dec). Szczególnie w pętlach for(..) w których często jest to obojętne.
7. W przypadku kodowania zawodowego, standardy wymagają aby operacje związane z kolejnością ewaluacji operatorów ujmować w nawiasy. Tak więc (linia 13) nie tak:
scanf("%d", tab[i][j]);
tylko tak:
scanf("%d", &(tab[i][j]));
Tak wiem że to uciążliwe, ale narzędzia sprawdzania kodu Ci to wytkną :-/
W efekcie kod może wyglądać tak (usunąłem część zakomentowaną):
#include <stdio.h>
#include <stdlib.h>
int main(void) {
unsigned liczbaTestow,iloscLiczb;
scanf("%d", &liczbaTestow);
int** tab = malloc(sizeof(*tab) * liczbaTestow);
for (unsigned i = 0; i < liczbaTestow; ++i) {
scanf("%d", &iloscLiczb);
tab[i] = malloc(sizeof(*tab) * iloscLiczb);
for (unsigned j = 0; j < iloscLiczb; ++j) {
scanf("%d", &(tab[i][j]));
}
for (unsigned k = 1; k < iloscLiczb; k += 2) {
printf("%d ", tab[i][k]);
}
for (unsigned k = 0; k < iloscLiczb; k += 2) {
printf("%d ", tab[i][k]);
}
putchar('\n');
}
for (unsigned i = 0; i < liczbaTestow; ++i) {
free(tab[i]);
}
free(tab);
return 0;
}
Teraz co do algorytmu.
1. Alokujesz dane ja tyle testów ile podano. Jeśli nie ma konieczności komunikowania się przez dane pomiędzy nimi, po co? Wystarczy alokowanie pamięci na 1 test. Ponowna alokacja możliwa za pomocą realloc(...). Większość systemów da Ci nawet to samo miejsce w pamięci. A dzięki temu unikniesz kłopotliwego czyszczenia tablic.
2. Kod prezentacji danych (parzyste/nie-parzyste pozycje) powtarza się. Ma tylko inny indeks startowy. Warto wyprowadzić go do funkcji. Podobnie wczytanie danych. W rzeczywistym projekcie jeśli pojawi się zarzut "bo to mała funkcja", zrób ją inline i static.